Move most logic into new StickVisualizer
class and add keyboard support.
This commit is contained in:
parent
11a2ea4843
commit
de08974f82
6 changed files with 332 additions and 114 deletions
123
src/Ryujinx/UI/Models/Input/StickVisualizer.cs
Normal file
123
src/Ryujinx/UI/Models/Input/StickVisualizer.cs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Ava.UI.Models.Input
|
||||||
|
{
|
||||||
|
public class StickVisualizer : BaseModel
|
||||||
|
{
|
||||||
|
public const int DrawStickPollRate = 50; // Milliseconds per poll.
|
||||||
|
public const int DrawStickCircumference = 5;
|
||||||
|
public const float DrawStickScaleFactor = DrawStickCanvasCenter;
|
||||||
|
public const int DrawStickCanvasSize = 100;
|
||||||
|
public const int DrawStickBorderSize = DrawStickCanvasSize + 5;
|
||||||
|
public const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
|
||||||
|
public const float MaxVectorLength = DrawStickCanvasSize / 2;
|
||||||
|
|
||||||
|
public CancellationTokenSource PollTokenSource = new();
|
||||||
|
public CancellationToken PollToken;
|
||||||
|
|
||||||
|
private static float _vectorLength;
|
||||||
|
private static float _vectorMultiplier;
|
||||||
|
|
||||||
|
private GamepadInputConfig _gamepadConfig;
|
||||||
|
public GamepadInputConfig GamepadConfig
|
||||||
|
{
|
||||||
|
get => _gamepadConfig;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_gamepadConfig = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private KeyboardInputConfig _keyboardConfig;
|
||||||
|
public KeyboardInputConfig KeyboardConfig
|
||||||
|
{
|
||||||
|
get => _keyboardConfig;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_keyboardConfig = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (float, float) _uiStickLeft;
|
||||||
|
public (float, float) UiStickLeft
|
||||||
|
{
|
||||||
|
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_uiStickLeft = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(UiStickRightX));
|
||||||
|
OnPropertyChanged(nameof(UiStickRightY));
|
||||||
|
OnPropertyChanged(nameof(UiDeadzoneRight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private (float, float) _uiStickRight;
|
||||||
|
public (float, float) UiStickRight
|
||||||
|
{
|
||||||
|
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_uiStickRight = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(UiStickLeftX));
|
||||||
|
OnPropertyChanged(nameof(UiStickLeftY));
|
||||||
|
OnPropertyChanged(nameof(UiDeadzoneLeft));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
|
||||||
|
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
|
||||||
|
public float UiStickRightX => ClampVector(UiStickRight).Item1;
|
||||||
|
public float UiStickRightY => ClampVector(UiStickRight).Item2;
|
||||||
|
|
||||||
|
public int UiStickCircumference => DrawStickCircumference;
|
||||||
|
public int UiCanvasSize => DrawStickCanvasSize;
|
||||||
|
public int UiStickBorderSize => DrawStickBorderSize;
|
||||||
|
|
||||||
|
public float? UiDeadzoneLeft => _gamepadConfig?.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
|
||||||
|
public float? UiDeadzoneRight => _gamepadConfig?.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
|
||||||
|
|
||||||
|
public void UpdateConfig(object config)
|
||||||
|
{
|
||||||
|
if (config is GamepadInputConfig padConfig)
|
||||||
|
{
|
||||||
|
GamepadConfig = padConfig;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (config is KeyboardInputConfig keyConfig)
|
||||||
|
{
|
||||||
|
KeyboardConfig = keyConfig;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ArgumentException($"Invalid configuration: {config}");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (float, float) ClampVector((float, float) vect)
|
||||||
|
{
|
||||||
|
_vectorMultiplier = 1;
|
||||||
|
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
|
||||||
|
|
||||||
|
if (_vectorLength > MaxVectorLength)
|
||||||
|
{
|
||||||
|
_vectorMultiplier = MaxVectorLength / _vectorLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
|
||||||
|
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
|
||||||
|
|
||||||
|
return vect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,6 @@ using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Models.Input;
|
using Ryujinx.Ava.UI.Models.Input;
|
||||||
using Ryujinx.Ava.UI.Views.Input;
|
using Ryujinx.Ava.UI.Views.Input;
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
@ -11,23 +10,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class ControllerInputViewModel : BaseModel
|
public class ControllerInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
private const int DrawStickPollRate = 50; // Milliseconds per poll.
|
|
||||||
private const int DrawStickCircumference = 5;
|
|
||||||
private const float DrawStickScaleFactor = DrawStickCanvasCenter;
|
|
||||||
|
|
||||||
private const int DrawStickCanvasSize = 100;
|
|
||||||
private const int DrawStickBorderSize = DrawStickCanvasSize + 5;
|
|
||||||
private const float DrawStickCanvasCenter = (DrawStickCanvasSize - DrawStickCircumference) / 2;
|
|
||||||
|
|
||||||
private const float MaxVectorLength = DrawStickCanvasSize / 2;
|
|
||||||
|
|
||||||
private IGamepad _selectedGamepad;
|
private IGamepad _selectedGamepad;
|
||||||
|
|
||||||
private float _vectorLength;
|
private StickVisualizer _stickVisualizer;
|
||||||
private float _vectorMultiplier;
|
public StickVisualizer StickVisualizer
|
||||||
|
{
|
||||||
|
get => _stickVisualizer;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_stickVisualizer = value;
|
||||||
|
|
||||||
internal CancellationTokenSource _pollTokenSource = new();
|
OnPropertyChanged();
|
||||||
private readonly CancellationToken _pollToken;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private GamepadInputConfig _config;
|
private GamepadInputConfig _config;
|
||||||
public GamepadInputConfig Config
|
public GamepadInputConfig Config
|
||||||
|
@ -36,6 +31,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_config = value;
|
_config = value;
|
||||||
|
StickVisualizer.UpdateConfig(Config);
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -77,48 +74,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private (float, float) _uiStickLeft;
|
|
||||||
public (float, float) UiStickLeft
|
|
||||||
{
|
|
||||||
get => (_uiStickLeft.Item1 * DrawStickScaleFactor, _uiStickLeft.Item2 * DrawStickScaleFactor);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_uiStickLeft = value;
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(UiStickRightX));
|
|
||||||
OnPropertyChanged(nameof(UiStickRightY));
|
|
||||||
OnPropertyChanged(nameof(UiDeadzoneRight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private (float, float) _uiStickRight;
|
|
||||||
public (float, float) UiStickRight
|
|
||||||
{
|
|
||||||
get => (_uiStickRight.Item1 * DrawStickScaleFactor, _uiStickRight.Item2 * DrawStickScaleFactor);
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_uiStickRight = value;
|
|
||||||
|
|
||||||
OnPropertyChanged();
|
|
||||||
OnPropertyChanged(nameof(UiStickLeftX));
|
|
||||||
OnPropertyChanged(nameof(UiStickLeftY));
|
|
||||||
OnPropertyChanged(nameof(UiDeadzoneLeft));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int UiStickCircumference => DrawStickCircumference;
|
|
||||||
public int UiCanvasSize => DrawStickCanvasSize;
|
|
||||||
public int UiStickBorderSize => DrawStickBorderSize;
|
|
||||||
|
|
||||||
public float UiStickLeftX => ClampVector(UiStickLeft).Item1;
|
|
||||||
public float UiStickLeftY => ClampVector(UiStickLeft).Item2;
|
|
||||||
public float UiStickRightX => ClampVector(UiStickRight).Item1;
|
|
||||||
public float UiStickRightY => ClampVector(UiStickRight).Item2;
|
|
||||||
|
|
||||||
public float UiDeadzoneLeft => Config.DeadzoneLeft * DrawStickCanvasSize - DrawStickCircumference;
|
|
||||||
public float UiDeadzoneRight => Config.DeadzoneRight * DrawStickCanvasSize - DrawStickCircumference;
|
|
||||||
|
|
||||||
public readonly InputViewModel ParentModel;
|
public readonly InputViewModel ParentModel;
|
||||||
|
|
||||||
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
|
public ControllerInputViewModel(InputViewModel model, GamepadInputConfig config)
|
||||||
|
@ -126,12 +81,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
ParentModel = model;
|
ParentModel = model;
|
||||||
model.NotifyChangesEvent += OnParentModelChanged;
|
model.NotifyChangesEvent += OnParentModelChanged;
|
||||||
OnParentModelChanged();
|
OnParentModelChanged();
|
||||||
|
_stickVisualizer = new();
|
||||||
Config = config;
|
Config = config;
|
||||||
|
|
||||||
_pollTokenSource = new();
|
StickVisualizer.PollToken = StickVisualizer.PollTokenSource.Token;
|
||||||
_pollToken = _pollTokenSource.Token;
|
|
||||||
|
|
||||||
Task.Run(() => PollSticks(_pollToken));
|
Task.Run(() => PollSticks(StickVisualizer.PollToken));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowMotionConfig()
|
public async void ShowMotionConfig()
|
||||||
|
@ -152,30 +107,14 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
|
|
||||||
if (_selectedGamepad != null && _selectedGamepad is not AvaloniaKeyboard)
|
if (_selectedGamepad != null && _selectedGamepad is not AvaloniaKeyboard)
|
||||||
{
|
{
|
||||||
UiStickLeft = _selectedGamepad.GetStick(StickInputId.Left);
|
StickVisualizer.UiStickLeft = _selectedGamepad.GetStick(StickInputId.Left);
|
||||||
UiStickRight = _selectedGamepad.GetStick(StickInputId.Right);
|
StickVisualizer.UiStickRight = _selectedGamepad.GetStick(StickInputId.Right);
|
||||||
}
|
}
|
||||||
|
|
||||||
await Task.Delay(DrawStickPollRate, token);
|
await Task.Delay(StickVisualizer.DrawStickPollRate, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
_pollTokenSource.Dispose();
|
StickVisualizer.PollTokenSource.Dispose();
|
||||||
}
|
|
||||||
|
|
||||||
private (float, float) ClampVector((float, float) vect)
|
|
||||||
{
|
|
||||||
_vectorMultiplier = 1;
|
|
||||||
_vectorLength = MathF.Sqrt((vect.Item1 * vect.Item1) + (vect.Item2 * vect.Item2));
|
|
||||||
|
|
||||||
if (_vectorLength > MaxVectorLength)
|
|
||||||
{
|
|
||||||
_vectorMultiplier = MaxVectorLength / _vectorLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
vect.Item1 = vect.Item1 * _vectorMultiplier + DrawStickCanvasCenter;
|
|
||||||
vect.Item2 = vect.Item2 * _vectorMultiplier + DrawStickCanvasCenter;
|
|
||||||
|
|
||||||
return vect;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnParentModelChanged()
|
public void OnParentModelChanged()
|
||||||
|
|
|
@ -879,7 +879,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
|
_mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected;
|
||||||
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
|
_mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected;
|
||||||
|
|
||||||
(ConfigViewModel as ControllerInputViewModel)?._pollTokenSource.Cancel();
|
(ConfigViewModel as ControllerInputViewModel)?.StickVisualizer.PollTokenSource.Cancel();
|
||||||
|
|
||||||
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
|
_mainWindow.ViewModel.AppHost?.NpadManager.UnblockInputUpdates();
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,27 @@
|
||||||
using Avalonia.Svg.Skia;
|
using Avalonia.Svg.Skia;
|
||||||
using Ryujinx.Ava.UI.Models.Input;
|
using Ryujinx.Ava.UI.Models.Input;
|
||||||
|
using Ryujinx.Input;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels.Input
|
namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
{
|
{
|
||||||
public class KeyboardInputViewModel : BaseModel
|
public class KeyboardInputViewModel : BaseModel
|
||||||
{
|
{
|
||||||
|
private (float, float) _leftBuffer = (0, 0);
|
||||||
|
private (float, float) _rightBuffer = (0, 0);
|
||||||
|
private StickVisualizer _stickVisualizer;
|
||||||
|
public StickVisualizer StickVisualizer
|
||||||
|
{
|
||||||
|
get => _stickVisualizer;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_stickVisualizer = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private KeyboardInputConfig _config;
|
private KeyboardInputConfig _config;
|
||||||
public KeyboardInputConfig Config
|
public KeyboardInputConfig Config
|
||||||
{
|
{
|
||||||
|
@ -12,6 +29,8 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_config = value;
|
_config = value;
|
||||||
|
StickVisualizer.UpdateConfig(_config);
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +79,65 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
|
||||||
ParentModel = model;
|
ParentModel = model;
|
||||||
model.NotifyChangesEvent += OnParentModelChanged;
|
model.NotifyChangesEvent += OnParentModelChanged;
|
||||||
OnParentModelChanged();
|
OnParentModelChanged();
|
||||||
|
_stickVisualizer = new();
|
||||||
Config = config;
|
Config = config;
|
||||||
|
|
||||||
|
StickVisualizer.PollToken = StickVisualizer.PollTokenSource.Token;
|
||||||
|
|
||||||
|
Task.Run(() => PollKeyboard(StickVisualizer.PollToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task PollKeyboard(CancellationToken token)
|
||||||
|
{
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
if (ParentModel.IsKeyboard)
|
||||||
|
{
|
||||||
|
IKeyboard keyboard = (IKeyboard)ParentModel.AvaloniaKeyboardDriver.GetGamepad("0");
|
||||||
|
var snap = keyboard.GetKeyboardStateSnapshot();
|
||||||
|
|
||||||
|
if (snap.IsPressed((Key)Config.LeftStickRight))
|
||||||
|
{
|
||||||
|
_leftBuffer.Item1 += 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.LeftStickLeft))
|
||||||
|
{
|
||||||
|
_leftBuffer.Item1 -= 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.LeftStickUp))
|
||||||
|
{
|
||||||
|
_leftBuffer.Item2 += 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.LeftStickDown))
|
||||||
|
{
|
||||||
|
_leftBuffer.Item2 -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (snap.IsPressed((Key)Config.RightStickRight))
|
||||||
|
{
|
||||||
|
_rightBuffer.Item1 += 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.RightStickLeft))
|
||||||
|
{
|
||||||
|
_rightBuffer.Item1 -= 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.RightStickUp))
|
||||||
|
{
|
||||||
|
_rightBuffer.Item2 += 1;
|
||||||
|
}
|
||||||
|
if (snap.IsPressed((Key)Config.RightStickDown))
|
||||||
|
{
|
||||||
|
_rightBuffer.Item2 -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
StickVisualizer.UiStickLeft = _leftBuffer;
|
||||||
|
StickVisualizer.UiStickRight = _rightBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(StickVisualizer.DrawStickPollRate, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
StickVisualizer.PollTokenSource.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnParentModelChanged()
|
public void OnParentModelChanged()
|
||||||
|
|
|
@ -333,72 +333,72 @@
|
||||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="5"
|
CornerRadius="5"
|
||||||
Height="{Binding UiStickBorderSize}"
|
Height="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
Width="{Binding UiStickBorderSize}"
|
Width="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
IsVisible="{Binding IsLeft}">
|
IsVisible="{Binding IsLeft}">
|
||||||
<Canvas
|
<Canvas
|
||||||
Background="{DynamicResource ThemeBackgroundColor}"
|
Background="{DynamicResource ThemeBackgroundColor}"
|
||||||
Height="{Binding UiCanvasSize}"
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Width="{Binding UiCanvasSize}">
|
Width="{Binding StickVisualizer.UiCanvasSize}">
|
||||||
<Grid
|
<Grid
|
||||||
Height="{Binding UiCanvasSize}"
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Width="{Binding UiCanvasSize}"
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Background="{DynamicResource ThemeBackgroundColor}">
|
Background="{DynamicResource ThemeBackgroundColor}">
|
||||||
<Ellipse
|
<Ellipse
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Stroke="Black"
|
Stroke="Black"
|
||||||
StrokeThickness="1"
|
StrokeThickness="1"
|
||||||
Width="{Binding UiCanvasSize}"
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Height="{Binding UiCanvasSize}"/>
|
Height="{Binding StickVisualizer.UiCanvasSize}"/>
|
||||||
<Ellipse
|
<Ellipse
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Fill="Gray"
|
Fill="Gray"
|
||||||
Opacity="100"
|
Opacity="100"
|
||||||
Height="{Binding UiDeadzoneLeft}"
|
Height="{Binding StickVisualizer.UiDeadzoneLeft}"
|
||||||
Width="{Binding UiDeadzoneLeft}"/>
|
Width="{Binding StickVisualizer.UiDeadzoneLeft}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Ellipse
|
<Ellipse
|
||||||
Fill="Red"
|
Fill="Red"
|
||||||
Width="{Binding UiStickCircumference}"
|
Width="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
Height="{Binding UiStickCircumference}"
|
Height="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
Canvas.Bottom="{Binding UiStickLeftY}"
|
Canvas.Bottom="{Binding StickVisualizer.UiStickLeftY}"
|
||||||
Canvas.Left="{Binding UiStickLeftX}" />
|
Canvas.Left="{Binding StickVisualizer.UiStickLeftX}" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</Border>
|
</Border>
|
||||||
<Border
|
<Border
|
||||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="5"
|
CornerRadius="5"
|
||||||
Height="{Binding UiStickBorderSize}"
|
Height="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
Width="{Binding UiStickBorderSize}"
|
Width="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
IsVisible="{Binding IsRight}">
|
IsVisible="{Binding IsRight}">
|
||||||
<Canvas
|
<Canvas
|
||||||
Background="{DynamicResource ThemeBackgroundColor}"
|
Background="{DynamicResource ThemeBackgroundColor}"
|
||||||
Height="{Binding UiCanvasSize}"
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Width="{Binding UiCanvasSize}">
|
Width="{Binding StickVisualizer.UiCanvasSize}">
|
||||||
<Grid
|
<Grid
|
||||||
Height="{Binding UiCanvasSize}"
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Width="{Binding UiCanvasSize}"
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Background="{DynamicResource ThemeBackgroundColor}">
|
Background="{DynamicResource ThemeBackgroundColor}">
|
||||||
<Ellipse
|
<Ellipse
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Stroke="Black"
|
Stroke="Black"
|
||||||
StrokeThickness="1"
|
StrokeThickness="1"
|
||||||
Width="{Binding UiCanvasSize}"
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
Height="{Binding UiCanvasSize}"/>
|
Height="{Binding StickVisualizer.UiCanvasSize}"/>
|
||||||
<Ellipse
|
<Ellipse
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
Fill="Gray"
|
Fill="Gray"
|
||||||
Opacity="100"
|
Opacity="100"
|
||||||
Height="{Binding UiDeadzoneRight}"
|
Height="{Binding StickVisualizer.UiDeadzoneRight}"
|
||||||
Width="{Binding UiDeadzoneRight}"/>
|
Width="{Binding StickVisualizer.UiDeadzoneRight}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Ellipse
|
<Ellipse
|
||||||
Fill="Red"
|
Fill="Red"
|
||||||
Width="{Binding UiStickCircumference}"
|
Width="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
Height="{Binding UiStickCircumference}"
|
Height="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
Canvas.Bottom="{Binding UiStickRightY}"
|
Canvas.Bottom="{Binding StickVisualizer.UiStickRightY}"
|
||||||
Canvas.Left="{Binding UiStickRightX}" />
|
Canvas.Left="{Binding StickVisualizer.UiStickRightX}" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</Border>
|
</Border>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
|
@ -312,12 +312,91 @@
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch">
|
VerticalAlignment="Stretch">
|
||||||
<!-- Controller Picture -->
|
<!-- Controller Picture -->
|
||||||
<Image
|
<Border
|
||||||
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="5"
|
||||||
Margin="0,10"
|
Margin="0,10"
|
||||||
MaxHeight="300"
|
MinHeight="90">
|
||||||
HorizontalAlignment="Stretch"
|
<StackPanel
|
||||||
VerticalAlignment="Stretch"
|
Margin="10"
|
||||||
Source="{Binding Image}" />
|
Orientation="Horizontal"
|
||||||
|
Spacing="20"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
|
<Border
|
||||||
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="5"
|
||||||
|
Height="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
|
IsVisible="{Binding IsLeft}">
|
||||||
|
<Canvas
|
||||||
|
Background="{DynamicResource ThemeBackgroundColor}"
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}">
|
||||||
|
<Grid
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Background="{DynamicResource ThemeBackgroundColor}">
|
||||||
|
<Ellipse
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Stroke="Black"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"/>
|
||||||
|
<Ellipse
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Fill="Gray"
|
||||||
|
Opacity="100"
|
||||||
|
Height="{Binding StickVisualizer.UiDeadzoneLeft}"
|
||||||
|
Width="{Binding StickVisualizer.UiDeadzoneLeft}"/>
|
||||||
|
</Grid>
|
||||||
|
<Ellipse
|
||||||
|
Fill="Red"
|
||||||
|
Width="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
|
Height="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
|
Canvas.Bottom="{Binding StickVisualizer.UiStickLeftY}"
|
||||||
|
Canvas.Left="{Binding StickVisualizer.UiStickLeftX}" />
|
||||||
|
</Canvas>
|
||||||
|
</Border>
|
||||||
|
<Border
|
||||||
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="5"
|
||||||
|
Height="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiStickBorderSize}"
|
||||||
|
IsVisible="{Binding IsRight}">
|
||||||
|
<Canvas
|
||||||
|
Background="{DynamicResource ThemeBackgroundColor}"
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}">
|
||||||
|
<Grid
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Background="{DynamicResource ThemeBackgroundColor}">
|
||||||
|
<Ellipse
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Stroke="Black"
|
||||||
|
StrokeThickness="1"
|
||||||
|
Width="{Binding StickVisualizer.UiCanvasSize}"
|
||||||
|
Height="{Binding StickVisualizer.UiCanvasSize}"/>
|
||||||
|
<Ellipse
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Fill="Gray"
|
||||||
|
Opacity="100"
|
||||||
|
Height="{Binding StickVisualizer.UiDeadzoneRight}"
|
||||||
|
Width="{Binding StickVisualizer.UiDeadzoneRight}"/>
|
||||||
|
</Grid>
|
||||||
|
<Ellipse
|
||||||
|
Fill="Red"
|
||||||
|
Width="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
|
Height="{Binding StickVisualizer.UiStickCircumference}"
|
||||||
|
Canvas.Bottom="{Binding StickVisualizer.UiStickRightY}"
|
||||||
|
Canvas.Left="{Binding StickVisualizer.UiStickRightX}" />
|
||||||
|
</Canvas>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
<Border
|
<Border
|
||||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
|
|
Loading…
Reference in a new issue