Compare commits

...

16 Commits

Author SHA1 Message Date
Vladimir Sokolov
f4e3029fcc Merge 1b7032b589 into e104ee6be3 2025-03-02 14:07:15 +00:00
Vova
1b7032b589 smal fix 2025-03-03 00:07:05 +10:00
Vova
e097ea71ff Fix: exclude device id when loading preset (independent presets)
fixed bug when selected gamepad disappears if another gamepad was disconnected
2025-03-02 23:49:40 +10:00
Vova
299f2144c8 Bug fixes, functionality improvements:
Now the profile changes immediately upon selection.
The icon for restoring settings has been changed.
A bug has been fixed where restoring settings did not restore the previously selected gamepad.
2025-03-02 19:07:49 +10:00
Vova
33e3ba9ff2 Fixed profiles on the input page:
- profiles are unlinked from controllers
- sometimes a new profile after saving changed to the previous one, had to select it again (fixed)
- when deleting, the profile now resets the name to "default"
2025-03-01 23:44:05 +10:00
Vladimir Sokolov
9dc36646c1 Merge branch 'master' into Master_PR 2025-02-28 20:54:11 +10:00
Danik2343
e104ee6be3 Update: Russian Language (Some missing strings) (#732) 2025-02-27 16:53:18 -06:00
Nicola
e65d1ec6c9 JoyCon to Joy-Con (#729)
Joy-Con is the official name
2025-02-26 20:00:35 -06:00
Evan Husted
534f92506b misc: chore: Add warning logs for invalid ips patch attempts 2025-02-26 02:31:18 -06:00
Vova
8eea75a6e8 Small fix 2025-02-23 16:44:10 +10:00
Vladimir Sokolov
57fbcc7aed Merge branch 'master' into Master_PR 2025-02-23 16:18:11 +10:00
Vova
d1c15f3562 Fixed a bug with the (undo last changes) button in the gamepad settings 2025-02-23 16:16:43 +10:00
Vova
0423fad7ff Merge branch 'Master_PR' of https://github.com/Goodfeat/Ryujinx_alt into Master_PR 2025-02-23 16:04:12 +10:00
Vova
1951fe0077 Added the ability to delete assigned buttons with the right mouse button in the settings.
- for keyboard
- for hotkeys
2025-02-23 15:59:03 +10:00
Vladimir Sokolov
7fd5a63a5d Merge branch 'master' into Master_PR 2025-02-23 14:18:24 +10:00
Vova
a0594e8169 Improved interaction with "Input" settings.
- paired devices have notifications that they are configured and require connection
- paired devices load the configuration when connected
- A notification appears when changing control configuration settings.
- Now control settings will be saved only when they are changed
- Added a button to roll back changes to the previously saved state
- Fixed a bug: when switching the "player", if the "input device" and "controller type" settings were changed, the save dialog box did not appear.
- "Motion", "Rumble" and "Led" also have events notifying about changes
2025-02-23 10:20:42 +10:00
11 changed files with 478 additions and 55 deletions

View File

@@ -97,7 +97,7 @@ If you are planning to contribute or just want to learn more about this project
- **Input** - **Input**
We currently have support for keyboard, mouse, touch input, JoyCon input support, and nearly all controllers. We currently have support for keyboard, mouse, touch input, Joy-Con input support, and nearly all controllers.
Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required. Motion controls are natively supported in most cases; for dual-JoyCon motion support, DS4Windows or BetterJoy are currently required.
In all scenarios, you can set up everything inside the input configuration menu. In all scenarios, you can set up everything inside the input configuration menu.

View File

@@ -71,16 +71,24 @@ namespace Ryujinx.HLE.Loaders.Mods
int patchOffset = (int)offset; int patchOffset = (int)offset;
int patchSize = patch.Length; int patchSize = patch.Length;
if (patchOffset < protectedOffset || patchOffset > memory.Length) if (patchOffset < protectedOffset)
{ {
continue; // Add warning? Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch protected memory ({patchOffset:x} is within protected boundary of {protectedOffset:x}).");
continue;
}
if (patchOffset > memory.Length)
{
Logger.Warning?.Print(LogClass.ModLoader, $"Attempted to patch out of bounds memory (offset {patchOffset} ({patchOffset:x}) exceeds memory buffer length {memory.Length}).");
continue;
} }
patchOffset -= protectedOffset; patchOffset -= protectedOffset;
if (patchOffset + patchSize > memory.Length) if (patchOffset + patchSize > memory.Length)
{ {
patchSize = memory.Length - patchOffset; // Add warning? Logger.Warning?.Print(LogClass.ModLoader, $"Patch offset ({patchOffset:x}) + size ({patchSize}) is greater than the size of the memory buffer ({memory.Length}). Attempting to fix this...");
patchSize = memory.Length - patchOffset;
} }
Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}"); Logger.Info?.Print(LogClass.ModLoader, $"Patching address offset {patchOffset:x} <= {BitConverter.ToString(patch).Replace('-', ' ')} len={patchSize}");

View File

@@ -49,7 +49,6 @@
<TextBlock <TextBlock
Classes="globalConfigMarker"/> Classes="globalConfigMarker"/>
</StackPanel> </StackPanel>
</Border> </Border>
</Design.PreviewWith> </Design.PreviewWith>
<Style Selector="DropDownButton"> <Style Selector="DropDownButton">

View File

@@ -2763,7 +2763,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Создать пользовательскую конфигурацию",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -2788,7 +2788,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Изменить пользовательскую конфигурацию",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -2863,7 +2863,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Отредактировать существующую независимую конфигурацию для выбранной игры.",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -5013,7 +5013,7 @@
"no_NO": "Lyd Inn/Ut", "no_NO": "Lyd Inn/Ut",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Выход/Вход звука",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",
@@ -6563,7 +6563,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Ок",
"sv_SE": "Ok", "sv_SE": "Ok",
"th_TH": "ตกลง", "th_TH": "ตกลง",
"tr_TR": "Tamam", "tr_TR": "Tamam",
@@ -7013,7 +7013,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "Pro Kontroler", "pl_PL": "Pro Kontroler",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Pro контроллер",
"sv_SE": "", "sv_SE": "",
"th_TH": "โปรคอนโทรลเลอร์", "th_TH": "โปรคอนโทรลเลอร์",
"tr_TR": "Profesyonel Kumanda", "tr_TR": "Profesyonel Kumanda",
@@ -7197,6 +7197,81 @@
"zh_TW": "新增" "zh_TW": "新增"
} }
}, },
{
"ID": "ControllerSettingsModifiedNotification",
"Translations": {
"ar_SA": "(تم التعديل!)",
"de_DE": "(modifiziert!)",
"el_GR": "(τροποποιημένο!)",
"en_US": "(Modified!)",
"es_ES": "(modificado!)",
"fr_FR": "(modifié!)",
"he_IL": "(שונה!)",
"it_IT": "(modificato!)",
"ja_JP": "(変更済み!)",
"ko_KR": "(수정됨!)",
"no_NO": "(modifisert!)",
"pl_PL": "(zmodyfikowane!)",
"pt_BR": "(modificado!)",
"ru_RU": "(изменено!)",
"sv_SE": "(ändrad!)",
"th_TH": "(แก้ไขแล้ว!)",
"tr_TR": "(değiştirildi!)",
"uk_UA": "(модифіковано!)",
"zh_CN": "(已修改!)",
"zh_TW": "(已修改!)"
}
},
{
"ID": "ControllerSettingsDisableDeviceForSaving",
"Translations": {
"ar_SA": "تم إعداد التحكم.\n\nفي انتظار اتصال وحدة التحكم...",
"de_DE": "Steuerung konfiguriert.\n\nWarten auf die Verbindung des Controllers...",
"el_GR": "Η διαχείριση έχει ρυθμιστεί.\n\nΑναμένεται σύνδεση του χειριστηρίου...",
"en_US": "Control configured.\n\nWaiting for controller connection...",
"es_ES": "Control configurado.\n\nEsperando la conexión del controlador...",
"fr_FR": "Contrôle configuré.\n\nEn attente de la connexion du contrôleur...",
"he_IL": "השליטה הוגדרה.\n\nממתין לחיבור הבקר...",
"it_IT": "Controllo configurato.\n\nIn attesa della connessione del controller...",
"ja_JP": "コントロールが設定されました。\n\nコントローラーの接続を待っています...",
"ko_KR": "제어가 설정되었습니다.\n\n컨트롤러 연결 대기 중...",
"no_NO": "Kontroll konfigurert.\n\nVenter på tilkobling av kontroller...",
"pl_PL": "Sterowanie skonfigurowane.\n\nOczekiwanie na połączenie kontrolera...",
"pt_BR": "Controle configurado.\n\nAguardando conexão do controle...",
"ru_RU": "Управление настроено.\n\nОжидается подключение контроллера...",
"sv_SE": "Kontroll konfigurerad.\n\nVäntar på anslutning av kontrollen...",
"th_TH": "การควบคุมได้รับการตั้งค่าแล้ว\n\nกำลังรอการเชื่อมต่อคอนโทรลเลอร์...",
"tr_TR": "Kontrol yapılandırıldı.\n\nKontrolcü bağlantısı bekleniyor...",
"uk_UA": "Керування налаштовано.\n\nОчікується підключення контролера...",
"zh_CN": "控制已配置。\n\n等待控制器连接...",
"zh_TW": "控制已設定。\n\n等待控制器連接..."
}
},
{
"ID": "ControllerSettingsUnlink",
"Translations": {
"ar_SA": "إلغاء الربط",
"de_DE": "Entkoppeln",
"el_GR": "Αποσύνδεση",
"en_US": "Unlink",
"es_ES": "Desvincular",
"fr_FR": "Dissocier",
"he_IL": "ניתוק קישור",
"it_IT": "Scollega",
"ja_JP": "リンク解除",
"ko_KR": "연결 해제",
"no_NO": "Frakoble",
"pl_PL": "Odłącz",
"pt_BR": "Desvincular",
"ru_RU": "Отвязать",
"sv_SE": "Koppla från",
"th_TH": "ยกเลิกการเชื่อมโยง",
"tr_TR": "Bağlantıyı Kes",
"uk_UA": "Відв'язати",
"zh_CN": "解除绑定",
"zh_TW": "解除綁定"
}
},
{ {
"ID": "ControllerSettingsRemove", "ID": "ControllerSettingsRemove",
"Translations": { "Translations": {
@@ -11847,6 +11922,31 @@
"zh_TW": "儲存設定檔" "zh_TW": "儲存設定檔"
} }
}, },
{
"ID": "ControllerSettingsCancelCurrentChangesToolTip",
"Translations": {
"ar_SA": "إلغاء التغييرات الحالية",
"de_DE": "Aktuelle Änderungen abbrechen",
"el_GR": "Ακύρωση τρεχουσών αλλαγών",
"en_US": "Cancel current changes",
"es_ES": "Cancelar los cambios actuales",
"fr_FR": "Annuler les modifications en cours",
"he_IL": "ביטול השינויים הנוכחיים",
"it_IT": "Annulla le modifiche correnti",
"ja_JP": "現在の変更をキャンセル",
"ko_KR": "현재 변경 취소",
"no_NO": "Avbryt gjeldende endringer",
"pl_PL": "Anuluj bieżące zmiany",
"pt_BR": "Cancelar alterações atuais",
"ru_RU": "Отменить текущие изменения",
"sv_SE": "Avbryt aktuella ändringar",
"th_TH": "ยกเลิกการเปลี่ยนแปลงปัจจุบัน",
"tr_TR": "Geçerli değişiklikleri iptal et",
"uk_UA": "Скасувати поточні зміни",
"zh_CN": "取消当前更改",
"zh_TW": "取消當前變更"
}
},
{ {
"ID": "MenuBarFileToolsTakeScreenshot", "ID": "MenuBarFileToolsTakeScreenshot",
"Translations": { "Translations": {
@@ -20013,7 +20113,7 @@
"no_NO": "", "no_NO": "",
"pl_PL": "", "pl_PL": "",
"pt_BR": "", "pt_BR": "",
"ru_RU": "", "ru_RU": "Амибо",
"sv_SE": "", "sv_SE": "",
"th_TH": "", "th_TH": "",
"tr_TR": "", "tr_TR": "",

View File

@@ -68,18 +68,21 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
public async void ShowMotionConfig() public async void ShowMotionConfig()
{ {
await MotionInputView.Show(this); await MotionInputView.Show(this);
ParentModel.IsModified = true;
} }
public async void ShowRumbleConfig() public async void ShowRumbleConfig()
{ {
await RumbleInputView.Show(this); await RumbleInputView.Show(this);
ParentModel.IsModified = true;
} }
public async void ShowLedConfig() public async void ShowLedConfig()
{ {
await LedInputView.Show(this); await LedInputView.Show(this);
ParentModel.IsModified = true;
} }
public void OnParentModelChanged() public void OnParentModelChanged()

View File

@@ -87,13 +87,40 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
public bool IsKeyboard => !IsController; public bool IsKeyboard => !IsController;
public bool IsRight { get; set; } public bool IsRight { get; set; }
public bool IsLeft { get; set; } public bool IsLeft { get; set; }
public int DeviceIndexBeforeChange { get; set; }
public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led); public bool HasLed => SelectedGamepad.Features.HasFlag(GamepadFeaturesFlag.Led);
public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense"); public bool CanClearLed => SelectedGamepad.Name.ContainsIgnoreCase("DualSense");
public bool IsModified { get; set; } public bool _isChangeTrackingActive;
public bool _isModified;
public bool IsModified
{
get => _isModified;
set
{
_isModified = value;
OnPropertyChanged();
}
}
public event Action NotifyChangesEvent; public event Action NotifyChangesEvent;
public string _profileChoose;
public string ProfileChoose
{
get => _profileChoose;
set
{
// When you select a profile, the settings from the profile will be applied.
// To save the settings, you still need to click the apply button
_profileChoose = value;
LoadProfile();
OnPropertyChanged();
}
}
public PlayerIndex PlayerIdChoose public PlayerIndex PlayerIdChoose
{ {
get => _playerIdChoose; get => _playerIdChoose;
@@ -106,14 +133,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
set set
{ {
if (IsModified) if (IsModified)
{ {
_playerIdChoose = value; _playerIdChoose = value;
return; return;
} }
IsModified = false; IsModified = false;
_playerId = value; _playerId = value;
_isChangeTrackingActive = false;
if (!Enum.IsDefined<PlayerIndex>(_playerId)) if (!Enum.IsDefined<PlayerIndex>(_playerId))
{ {
@@ -121,13 +148,13 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
_isLoaded = false; _isLoaded = false;
LoadConfiguration(); LoadConfiguration();
LoadDevice(); LoadDevice();
LoadProfiles(); LoadProfiles();
DeviceIndexBeforeChange = Device;
_isLoaded = true; _isLoaded = true;
_isChangeTrackingActive = true;
OnPropertyChanged(); OnPropertyChanged();
} }
} }
@@ -171,11 +198,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
IsLeft = false; IsLeft = false;
break; break;
} }
LoadInputDriver(); LoadInputDriver();
LoadProfiles(); LoadProfiles();
SetChangeTrackingActive();
} }
OnPropertyChanged(); OnPropertyChanged();
NotifyChanges(); NotifyChanges();
} }
@@ -215,6 +243,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
get => _device; get => _device;
set set
{ {
if (!IsModified)
{
DeviceIndexBeforeChange = _device;
}
_device = value < 0 ? 0 : value; _device = value < 0 ? 0 : value;
if (_device >= Devices.Count) if (_device >= Devices.Count)
@@ -234,13 +267,28 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
FindPairedDevice();
SetChangeTrackingActive();
OnPropertyChanged(); OnPropertyChanged();
NotifyChanges(); NotifyChanges();
} }
} }
public InputConfig Config { get; set; } public InputConfig Config { get; set; }
public bool _notificationView;
public bool NotificationView
{
get => _notificationView;
set
{
_notificationView = value;
OnPropertyChanged();
}
}
public InputViewModel(UserControl owner) : this() public InputViewModel(UserControl owner) : this()
{ {
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
@@ -260,6 +308,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
PlayerId = PlayerIndex.Player1; PlayerId = PlayerIndex.Player1;
} }
_isChangeTrackingActive = true;
} }
public InputViewModel() public InputViewModel()
@@ -296,8 +346,54 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig)); ConfigViewModel = new ControllerInputViewModel(this, new GamepadInputConfig(controllerInputConfig));
} }
FindPairedDevice();
} }
private void FindPairedDevice()
{
// This feature allows you to display a notification
// if a configuration is found, but the gamepad is not connected.
if (Config != null)
{
(DeviceType Type, string Id, string Name) activeDevice = Devices.FirstOrDefault(d => d.Id == Config.Id);
if (activeDevice.Id != Config.Id)
{
// display notification when input device is turned off, and
// if device and configuration do not match (different controllers)
NotificationView = true;
}
else
{
NotificationView = false;
}
}
else
{
NotificationView = false;
}
}
private void SetChangeTrackingActive()
{
if (_isChangeTrackingActive)
{
IsModified = true;
}
}
public void DisableDeviceForSaving()
{
// "Disabled" mode is available after unbinding the device
// NOTE: the IsModified flag to be able to apply the settings.
IsModified = true;
NotificationView = false;
}
public void LoadDevice() public void LoadDevice()
{ {
if (Config == null || Config.Backend == InputBackendType.Invalid) if (Config == null || Config.Backend == InputBackendType.Invalid)
@@ -363,14 +459,37 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
private void HandleOnGamepadDisconnected(string id) private async void HandleOnGamepadDisconnected(string id)
{ {
Dispatcher.UIThread.Post(LoadDevices); _isChangeTrackingActive = false;
await Dispatcher.UIThread.InvokeAsync(() =>
{
LoadDevices();
IsModified = true;
LoadSavedConfiguration();
FindPairedDevice();
_isChangeTrackingActive = true;
return System.Threading.Tasks.Task.CompletedTask;
});
} }
private void HandleOnGamepadConnected(string id) private async void HandleOnGamepadConnected(string id)
{ {
Dispatcher.UIThread.Post(LoadDevices); _isChangeTrackingActive = false;
await Dispatcher.UIThread.InvokeAsync(() =>
{
LoadDevices();
if (Config != null)
{
// Load configuration after connection if it is in the configuration file
IsModified = true;
LoadSavedConfiguration();
}
_isChangeTrackingActive = true;
});
} }
private string GetCurrentGamepadId() private string GetCurrentGamepadId()
@@ -673,6 +792,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
return config; return config;
} }
public void LoadProfileButton()
{
IsModified = true;
LoadProfile();
}
public async void LoadProfile() public async void LoadProfile()
{ {
if (Device == 0) if (Device == 0)
@@ -724,9 +849,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
_isLoaded = false; _isLoaded = false;
config.Id = null; // ignore device IDs (there is no longer a need to store device IDs for presets due to their independence from devices)
LoadConfiguration(config); LoadConfiguration(config);
LoadDevice(); // This line of code hard-links profiles to controllers, the commented line allows profiles to be applied to all controllers
// LoadDevice();
_isLoaded = true; _isLoaded = true;
@@ -778,6 +906,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
await File.WriteAllTextAsync(path, jsonString); await File.WriteAllTextAsync(path, jsonString);
LoadProfiles(); LoadProfiles();
ProfileChoose = ProfileName; // Show new profile
} }
else else
{ {
@@ -810,14 +940,40 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
LoadProfiles(); LoadProfiles();
ProfileChoose = ProfilesList[0].ToString(); // Show default profile
}
}
public void LoadSavedConfiguration()
{
// Restores settings and sets the previously selected device to the last saved state
// NOTE: The current order allows the configuration and device to be loaded correctly until the configuration is changed.
if (IsModified) // Fixes random gamepad appearance in "disabled" option
{
Device = DeviceIndexBeforeChange;
LoadDevice();
LoadConfiguration();
IsModified = false;
OnPropertyChanged();
} }
} }
public void Save() public void Save()
{ {
IsModified = false;
if (!IsModified)
{
return; //If the input settings were not touched, then do nothing
}
List<InputConfig> newConfig = []; IsModified = false;
DeviceIndexBeforeChange = Device;
List <InputConfig> newConfig = [];
newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value);

View File

@@ -63,8 +63,9 @@ namespace Ryujinx.Ava.UI.Views.Input
}; };
if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value) if (!float.IsNaN(_changeSlider) && _changeSlider != (float)check.Value)
{ {
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true; FlagInputConfigChanged();
_changeSlider = (float)check.Value; _changeSlider = (float)check.Value;
} }
} }
@@ -74,7 +75,8 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
if (sender is CheckBox { IsPointerOver: true }) if (sender is CheckBox { IsPointerOver: true })
{ {
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true; FlagInputConfigChanged();
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
} }
@@ -101,7 +103,7 @@ namespace Ryujinx.Ava.UI.Views.Input
this.Focus(NavigationMethod.Pointer); this.Focus(NavigationMethod.Pointer);
PointerPressed += MouseClick; PointerPressed += MouseClick;
ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel); ControllerInputViewModel viewModel = (DataContext as ControllerInputViewModel);
IKeyboard keyboard = IKeyboard keyboard =
@@ -114,7 +116,7 @@ namespace Ryujinx.Ava.UI.Views.Input
if (e.ButtonValue.HasValue) if (e.ButtonValue.HasValue)
{ {
Button buttonValue = e.ButtonValue.Value; Button buttonValue = e.ButtonValue.Value;
viewModel.ParentModel.IsModified = true; FlagInputConfigChanged();
switch (button.Name) switch (button.Name)
{ {
@@ -208,6 +210,11 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
} }
private void FlagInputConfigChanged()
{
(DataContext as ControllerInputViewModel)!.ParentModel.IsModified = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
@@ -239,7 +246,6 @@ namespace Ryujinx.Ava.UI.Views.Input
{ {
gamepad?.ClearLed(); gamepad?.ClearLed();
} }
_currentAssigner?.Cancel(); _currentAssigner?.Cancel();
_currentAssigner = null; _currentAssigner = null;
} }

View File

@@ -50,13 +50,21 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<TextBlock <StackPanel
Orientation="Vertical"
Margin="5,0,10,0" Margin="5,0,10,0"
Width="90"
HorizontalAlignment="Left" HorizontalAlignment="Left"
VerticalAlignment="Center" VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsPlayer}" /> Width="90">
<TextBlock
Text="{ext:Locale ControllerSettingsPlayer}" />
<TextBlock
Classes="pending"
Text ="{ext:Locale ControllerSettingsModifiedNotification}"
IsVisible="{Binding IsModified}"/>
</StackPanel>
<ComboBox <ComboBox
Grid.Column="1" Grid.Column="1"
Name="PlayerIndexBox" Name="PlayerIndexBox"
@@ -71,6 +79,18 @@
</DataTemplate> </DataTemplate>
</ComboBox.ItemTemplate> </ComboBox.ItemTemplate>
</ComboBox> </ComboBox>
<Button
Grid.Column="2"
MinWidth="0"
Margin="5,0,0,0"
VerticalAlignment="Center"
ToolTip.Tip="{ext:Locale ControllerSettingsCancelCurrentChangesToolTip}"
Command="{Binding LoadSavedConfiguration}">
<ui:SymbolIcon
Symbol="Undo"
FontSize="15"
Height="20" />
</Button>
</Grid> </Grid>
<!-- Profile Selection --> <!-- Profile Selection -->
<Grid <Grid
@@ -97,7 +117,8 @@
Name="ProfileBox" Name="ProfileBox"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Center" VerticalAlignment="Center"
SelectedIndex="0" SelectedItem="{Binding ProfileChoose, Mode=TwoWay}"
SelectionChanged="ComboBox_SelectionChanged"
ItemsSource="{Binding ProfilesList}" ItemsSource="{Binding ProfilesList}"
Text="{Binding ProfileName, Mode=TwoWay}" /> Text="{Binding ProfileName, Mode=TwoWay}" />
<Button <Button
@@ -106,7 +127,7 @@
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}" ToolTip.Tip="{ext:Locale ControllerSettingsLoadProfileToolTip}"
Command="{Binding LoadProfile}"> Command="{Binding LoadProfileButton}">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="View" Symbol="View"
FontSize="15" FontSize="15"
@@ -174,7 +195,7 @@
MinWidth="0" MinWidth="0"
Margin="5,0,0,0" Margin="5,0,0,0"
VerticalAlignment="Center" VerticalAlignment="Center"
Command="{Binding LoadDevices}"> Command="{Binding LoadDevice}">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="Refresh" Symbol="Refresh"
FontSize="15" FontSize="15"
@@ -211,15 +232,37 @@
</Grid> </Grid>
</Grid> </Grid>
</StackPanel> </StackPanel>
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}"> <ContentControl IsVisible="{Binding NotificationView}">
<ContentControl.DataTemplates> <ContentControl.Content>
<DataTemplate DataType="viewModels:ControllerInputViewModel"> <StackPanel>
<views:ControllerInputView /> <TextBlock
</DataTemplate> Margin="5,20,0,0"
<DataTemplate DataType="viewModels:KeyboardInputViewModel"> Text="{ext:Locale ControllerSettingsDisableDeviceForSaving}" />
<views:KeyboardInputView />
</DataTemplate> <Button
</ContentControl.DataTemplates> MinWidth="0"
Width="90"
Height="27"
Margin="5,10,0,0"
VerticalAlignment="Center"
Command="{Binding DisableDeviceForSaving}">
<TextBlock
Text="{ext:Locale ControllerSettingsUnlink}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
</Button>
</StackPanel>
</ContentControl.Content>
</ContentControl> </ContentControl>
<ContentControl Content="{Binding ConfigViewModel}" IsVisible="{Binding ShowSettings}">
<ContentControl.DataTemplates>
<DataTemplate DataType="viewModels:ControllerInputViewModel">
<views:ControllerInputView />
</DataTemplate>
<DataTemplate DataType="viewModels:KeyboardInputViewModel">
<views:KeyboardInputView />
</DataTemplate>
</ContentControl.DataTemplates>
</ContentControl>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

View File

@@ -1,4 +1,5 @@
using Avalonia.Controls; using Avalonia.Controls;
using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
@@ -62,14 +63,23 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
return; return;
} }
ViewModel.PlayerId = ViewModel.PlayerIdChoose;
ViewModel.IsModified = false; ViewModel.IsModified = false;
} ViewModel.PlayerId = ViewModel.PlayerIdChoose;
}
} }
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is FAComboBox faComboBox)
{
faComboBox.IsDropDownOpen = false;
ViewModel.IsModified = true;
}
}
public void Dispose() public void Dispose()
{ {
ViewModel.Dispose(); ViewModel.Dispose();

View File

@@ -8,6 +8,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels.Input; using Ryujinx.Ava.UI.ViewModels.Input;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.Assigner; using Ryujinx.Input.Assigner;
using System.Collections.Generic;
using System;
using Button = Ryujinx.Input.Button; using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key; using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -73,7 +75,7 @@ namespace Ryujinx.Ava.UI.Views.Input
if (e.ButtonValue.HasValue) if (e.ButtonValue.HasValue)
{ {
Button buttonValue = e.ButtonValue.Value; Button buttonValue = e.ButtonValue.Value;
viewModel.ParentModel.IsModified = true; FlagInputConfigChanged();
switch (button.Name) switch (button.Name)
{ {
@@ -184,15 +186,74 @@ namespace Ryujinx.Ava.UI.Views.Input
} }
} }
private void FlagInputConfigChanged()
{
(DataContext as KeyboardInputViewModel)!.ParentModel.IsModified = true;
}
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
if (shouldRemoveBinding)
{
DeleteBind();
}
_currentAssigner?.Cancel(shouldUnbind); _currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick; PointerPressed -= MouseClick;
} }
private void DeleteBind()
{
if (DataContext is not KeyboardInputViewModel viewModel)
return;
if (_currentAssigner != null)
{
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
{
{ "ButtonZl", () => viewModel.Config.ButtonZl = Key.Unbound },
{ "ButtonL", () => viewModel.Config.ButtonL = Key.Unbound },
{ "ButtonMinus", () => viewModel.Config.ButtonMinus = Key.Unbound },
{ "LeftStickButton", () => viewModel.Config.LeftStickButton = Key.Unbound },
{ "LeftStickUp", () => viewModel.Config.LeftStickUp = Key.Unbound },
{ "LeftStickDown", () => viewModel.Config.LeftStickDown = Key.Unbound },
{ "LeftStickRight", () => viewModel.Config.LeftStickRight = Key.Unbound },
{ "LeftStickLeft", () => viewModel.Config.LeftStickLeft = Key.Unbound },
{ "DpadUp", () => viewModel.Config.DpadUp = Key.Unbound },
{ "DpadDown", () => viewModel.Config.DpadDown = Key.Unbound },
{ "DpadLeft", () => viewModel.Config.DpadLeft = Key.Unbound },
{ "DpadRight", () => viewModel.Config.DpadRight = Key.Unbound },
{ "LeftButtonSr", () => viewModel.Config.LeftButtonSr = Key.Unbound },
{ "LeftButtonSl", () => viewModel.Config.LeftButtonSl = Key.Unbound },
{ "RightButtonSr", () => viewModel.Config.RightButtonSr = Key.Unbound },
{ "RightButtonSl", () => viewModel.Config.RightButtonSl = Key.Unbound },
{ "ButtonZr", () => viewModel.Config.ButtonZr = Key.Unbound },
{ "ButtonR", () => viewModel.Config.ButtonR = Key.Unbound },
{ "ButtonPlus", () => viewModel.Config.ButtonPlus = Key.Unbound },
{ "ButtonA", () => viewModel.Config.ButtonA = Key.Unbound },
{ "ButtonB", () => viewModel.Config.ButtonB = Key.Unbound },
{ "ButtonX", () => viewModel.Config.ButtonX = Key.Unbound },
{ "ButtonY", () => viewModel.Config.ButtonY = Key.Unbound },
{ "RightStickButton", () => viewModel.Config.RightStickButton = Key.Unbound },
{ "RightStickUp", () => viewModel.Config.RightStickUp = Key.Unbound },
{ "RightStickDown", () => viewModel.Config.RightStickDown = Key.Unbound },
{ "RightStickRight", () => viewModel.Config.RightStickRight = Key.Unbound },
{ "RightStickLeft", () => viewModel.Config.RightStickLeft = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
{
action();
FlagInputConfigChanged();
}
}
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e) protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{ {
base.OnDetachedFromVisualTree(e); base.OnDetachedFromVisualTree(e);

View File

@@ -8,6 +8,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.Assigner; using Ryujinx.Input.Assigner;
using System.Collections.Generic;
using System;
using Button = Ryujinx.Input.Button; using Button = Ryujinx.Input.Button;
using Key = Ryujinx.Common.Configuration.Hid.Key; using Key = Ryujinx.Common.Configuration.Hid.Key;
@@ -46,12 +48,47 @@ namespace Ryujinx.Ava.UI.Views.Settings
private void MouseClick(object sender, PointerPressedEventArgs e) private void MouseClick(object sender, PointerPressedEventArgs e)
{ {
bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed; bool shouldUnbind = e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed;
bool shouldRemoveBinding = e.GetCurrentPoint(this).Properties.IsRightButtonPressed;
if (shouldRemoveBinding)
{
DeleteBind();
}
_currentAssigner?.Cancel(shouldUnbind); _currentAssigner?.Cancel(shouldUnbind);
PointerPressed -= MouseClick; PointerPressed -= MouseClick;
} }
private void DeleteBind()
{
if (DataContext is not SettingsViewModel viewModel)
return;
if (_currentAssigner != null)
{
Dictionary<string, Action> buttonActions = new Dictionary<string, Action>
{
{ "ToggleVSyncMode", () => viewModel.KeyboardHotkey.ToggleVSyncMode = Key.Unbound },
{ "Screenshot", () => viewModel.KeyboardHotkey.Screenshot = Key.Unbound },
{ "ShowUI", () => viewModel.KeyboardHotkey.ShowUI = Key.Unbound },
{ "Pause", () => viewModel.KeyboardHotkey.Pause = Key.Unbound },
{ "ToggleMute", () => viewModel.KeyboardHotkey.ToggleMute = Key.Unbound },
{ "ResScaleUp", () => viewModel.KeyboardHotkey.ResScaleUp = Key.Unbound },
{ "ResScaleDown", () => viewModel.KeyboardHotkey.ResScaleDown = Key.Unbound },
{ "VolumeUp", () => viewModel.KeyboardHotkey.VolumeUp = Key.Unbound },
{ "VolumeDown", () => viewModel.KeyboardHotkey.VolumeDown = Key.Unbound },
{ "CustomVSyncIntervalIncrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = Key.Unbound },
{ "CustomVSyncIntervalDecrement", () => viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = Key.Unbound }
};
if (buttonActions.TryGetValue(_currentAssigner.ToggledButton.Name, out Action action))
{
action();
}
}
}
private void Button_IsCheckedChanged(object sender, RoutedEventArgs e) private void Button_IsCheckedChanged(object sender, RoutedEventArgs e)
{ {
if (sender is ToggleButton button) if (sender is ToggleButton button)