Initial implementation of analog stick visualization.
This commit is contained in:
parent
1a0a351a15
commit
0682f6eea9
3 changed files with 143 additions and 5 deletions
|
@ -1,11 +1,28 @@
|
|||
using Avalonia.Svg.Skia;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Models.Input;
|
||||
using Ryujinx.Ava.UI.Views.Input;
|
||||
using Ryujinx.Input;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||
{
|
||||
public class ControllerInputViewModel : BaseModel
|
||||
{
|
||||
private const int StickUiPollMs = 50; // Milliseconds per poll.
|
||||
private const float CanvasCenterOffset = 75f/2f;
|
||||
private const int StickScaleFactor = 30;
|
||||
|
||||
private IGamepad _selectedGamepad;
|
||||
|
||||
// Offset from origin for UI stick visualization.
|
||||
private (float, float) _uiStickLeft;
|
||||
private (float, float) _uiStickRight;
|
||||
|
||||
internal CancellationTokenSource _pollTokenSource = new();
|
||||
private CancellationToken _pollToken;
|
||||
|
||||
private GamepadInputConfig _config;
|
||||
public GamepadInputConfig Config
|
||||
{
|
||||
|
@ -54,6 +71,39 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
}
|
||||
}
|
||||
|
||||
public (float, float) UiStickLeft
|
||||
{
|
||||
get => (_uiStickLeft.Item1 * StickScaleFactor, _uiStickLeft.Item2 * StickScaleFactor);
|
||||
set
|
||||
{
|
||||
_uiStickLeft = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(UiStickRightX));
|
||||
OnPropertyChanged(nameof(UiStickRightY));
|
||||
}
|
||||
}
|
||||
|
||||
public (float, float) UiStickRight
|
||||
{
|
||||
get => (_uiStickRight.Item1 * StickScaleFactor, _uiStickRight.Item2 * StickScaleFactor);
|
||||
set
|
||||
{
|
||||
_uiStickRight = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(UiStickLeftX));
|
||||
OnPropertyChanged(nameof(UiStickLeftY));
|
||||
}
|
||||
}
|
||||
|
||||
public float canvasCenter => CanvasCenterOffset;
|
||||
|
||||
public float UiStickLeftX => UiStickLeft.Item1 + CanvasCenterOffset;
|
||||
public float UiStickLeftY => UiStickLeft.Item2 + CanvasCenterOffset;
|
||||
public float UiStickRightX => UiStickRight.Item1 + CanvasCenterOffset;
|
||||
public float UiStickRightY => UiStickRight.Item2 + CanvasCenterOffset;
|
||||
|
||||
public readonly InputViewModel ParentModel;
|
||||
|
||||
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
|
||||
|
@ -62,6 +112,11 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
model.NotifyChangesEvent += OnParentModelChanged;
|
||||
OnParentModelChanged();
|
||||
Config = config;
|
||||
|
||||
_pollTokenSource = new();
|
||||
_pollToken = _pollTokenSource.Token;
|
||||
|
||||
Task.Run(() => PollSticks(_pollToken));
|
||||
}
|
||||
|
||||
public async void ShowMotionConfig()
|
||||
|
@ -74,6 +129,24 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
await RumbleInputView.Show(this);
|
||||
}
|
||||
|
||||
private async Task PollSticks(CancellationToken token)
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
_selectedGamepad = ParentModel.SelectedGamepad;
|
||||
|
||||
if (_selectedGamepad != null && _selectedGamepad is not AvaloniaKeyboard)
|
||||
{
|
||||
UiStickLeft = _selectedGamepad.GetStick(StickInputId.Left);
|
||||
UiStickRight = _selectedGamepad.GetStick(StickInputId.Right);
|
||||
}
|
||||
|
||||
await Task.Delay(StickUiPollMs);
|
||||
}
|
||||
|
||||
_pollTokenSource.Dispose();
|
||||
}
|
||||
|
||||
public void OnParentModelChanged()
|
||||
{
|
||||
IsLeft = ParentModel.IsLeft;
|
||||
|
|
|
@ -879,6 +879,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
|||
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
|
||||
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
|
||||
|
||||
(ConfigViewModel as ControllerInputViewModel)._pollTokenSource?.Cancel();
|
||||
|
||||
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
|
||||
|
||||
SelectedGamepad?.Dispose();
|
||||
|
|
|
@ -318,12 +318,75 @@
|
|||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch">
|
||||
<!-- Controller Picture -->
|
||||
<Image
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Margin="0,10"
|
||||
MaxHeight="300"
|
||||
HorizontalAlignment="Stretch"
|
||||
VerticalAlignment="Stretch"
|
||||
Source="{Binding Image}" />
|
||||
MinHeight="90">
|
||||
<StackPanel
|
||||
Margin="10"
|
||||
Orientation="Horizontal"
|
||||
Spacing="20"
|
||||
HorizontalAlignment="Center">
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Height="100"
|
||||
Width="100">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="100"
|
||||
Width="100">
|
||||
<Ellipse
|
||||
Fill="Gray"
|
||||
Stroke="Blue"
|
||||
StrokeThickness="1"
|
||||
Width="25"
|
||||
Height="25"
|
||||
Canvas.Bottom="{Binding UiStickLeftY}"
|
||||
Canvas.Left="{Binding UiStickLeftX}" />
|
||||
<Ellipse
|
||||
Stroke="Black"
|
||||
StrokeThickness="1"
|
||||
StrokeDashOffset="2"
|
||||
Width="96"
|
||||
Height="96"
|
||||
Canvas.Bottom="2"
|
||||
Canvas.Left="2" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
CornerRadius="5"
|
||||
Height="100"
|
||||
Width="100">
|
||||
<Canvas
|
||||
Background="{DynamicResource ThemeBackgroundColor}"
|
||||
Height="100"
|
||||
Width="100">
|
||||
<Ellipse
|
||||
Fill="Gray"
|
||||
Stroke="Blue"
|
||||
StrokeThickness="1"
|
||||
Width="25"
|
||||
Height="25"
|
||||
Canvas.Bottom="{Binding UiStickRightY}"
|
||||
Canvas.Left="{Binding UiStickRightX}"/>
|
||||
<Ellipse
|
||||
Stroke="Black"
|
||||
StrokeThickness="1"
|
||||
StrokeDashOffset="2"
|
||||
Width="96"
|
||||
Height="96"
|
||||
Canvas.Bottom="2"
|
||||
Canvas.Left="2" />
|
||||
</Canvas>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
<Border
|
||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||
BorderThickness="1"
|
||||
|
|
Loading…
Reference in a new issue