Ryujinx/Ryujinx.Ava/Ui/Windows/ControllerSettingsWindow.axaml.cs
2022-05-15 12:44:58 +00:00

288 lines
No EOL
8.9 KiB
C#

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 _isWaitingForInput;
private bool _mousePressed;
private bool _middleMousePressed;
private bool _dialogOpen;
public Grid SettingButtons { get; set; }
public ToggleButton CurrentToggledButton { get; set; }
public 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 (CurrentToggledButton != null && !CurrentToggledButton.IsPointerOver)
{
ToggleButton button = CurrentToggledButton;
CurrentToggledButton = null;
button.IsChecked = false;
}
}
private void Button_Checked(object sender, RoutedEventArgs e)
{
if (sender is ToggleButton button)
{
if (button == CurrentToggledButton)
{
return;
}
if (CurrentToggledButton == null && (bool)button.IsChecked)
{
CurrentToggledButton = button;
_isWaitingForInput = false;
bool isStick = button.Tag != null && button.Tag.ToString() == "stick";
FocusManager.Instance.Focus(this, NavigationMethod.Pointer);
Task.Run(() => HandleButtonPressed(button, isStick));
}
else
{
if (CurrentToggledButton != null)
{
ToggleButton oldButton = CurrentToggledButton;
CurrentToggledButton = null;
oldButton.IsChecked = false;
button.IsChecked = false;
}
}
}
}
public void SaveCurrentProfile()
{
ViewModel.Save();
}
public void HandleButtonPressed(ToggleButton button, bool forStick)
{
if (_isWaitingForInput)
{
Dispatcher.UIThread.Post(() =>
{
button.IsChecked = false;
});
return;
}
_mousePressed = false;
_isWaitingForInput = true;
PointerPressed += MouseClick;
IButtonAssigner assigner = CreateButtonAssigner(forStick);
IKeyboard keyboard = (IKeyboard)ViewModel.AvaloniaKeyboardDriver.GetGamepad("0"); // Open Avalonia keyboard for cancel operations.
Thread inputThread = new(() =>
{
assigner.Initialize();
while (true)
{
if (!_isWaitingForInput)
{
return;
}
Thread.Sleep(10);
assigner.ReadInput();
if (_mousePressed || keyboard.IsPressed(Key.Escape) || assigner.HasAnyButtonPressed() || assigner.ShouldCancel())
{
break;
}
}
Dispatcher.UIThread.Post(() =>
{
string pressedButton = assigner.GetPressedButton();
if (_middleMousePressed)
{
try
{
SetButtonText(button, "Unbound");
}
catch { }
}
else if (pressedButton != "")
{
try
{
SetButtonText(button, pressedButton);
}
catch { }
}
ViewModel.IsModified = true;
keyboard.Dispose();
_middleMousePressed = false;
_isWaitingForInput = false;
button = CurrentToggledButton;
CurrentToggledButton = null;
if (button != null)
{
button.IsChecked = false;
}
PointerPressed -= MouseClick;
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;
}
}
});
});
inputThread.Name = "GUI.InputThread";
inputThread.IsBackground = true;
inputThread.Start();
}
private IButtonAssigner CreateButtonAssigner(bool forStick)
{
IButtonAssigner assigner;
string selected = ViewModel.Devices[ViewModel.Device].Id;
if (selected.StartsWith("keyboard"))
{
assigner = new KeyboardKeyAssigner((IKeyboard)ViewModel.SelectedGamepad);
}
else if (selected.StartsWith("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)
{
if (CurrentToggledButton != null)
{
ToggleButton button = CurrentToggledButton;
CurrentToggledButton = null;
button.IsChecked = false;
}
}
private void MouseClick(object sender, PointerPressedEventArgs e)
{
_mousePressed = true;
if (e.GetCurrentPoint(this).Properties.IsMiddleButtonPressed)
{
_middleMousePressed = true;
}
}
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()
{
ViewModel.Dispose();
}
}
}