Ava UI: Input Menu Redesign (#4990)

* Cleanup

* Remove redundant locales

* Start SVG Fixes…

Better +/- buttons

Fix the grips

Bumpers

Better directional pad

More SVG stuff

Grip adjustments

Final stuff

* Make image bigger

* Border radius

* More cleanup

* Restructure

* Restructure Rumble View

* Use compiled bindings where possible

* Round those pesky corners

* Ack Suggestions

* More suggestions

* Update src/Ryujinx.Ava/UI/Views/Input/RumbleInputView.axaml.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

---------

Co-authored-by: Ac_K <Acoustik666@gmail.com>
This commit is contained in:
Isaac Marovitz
2023-05-22 00:16:20 +01:00
committed by GitHub
parent ac66643346
commit b53e7ffd46
12 changed files with 731 additions and 436 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,181 +0,0 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Input;
using Ryujinx.Input.Assigner;
using System;
namespace Ryujinx.Ava.UI.Windows
{
public partial class ControllerSettingsWindow : UserControl
{
private bool _dialogOpen;
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;
}
}
}
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 == DeviceType.Keyboard)
{
assigner = new KeyboardKeyAssigner((IKeyboard)ViewModel.SelectedGamepad);
}
else if (device.Type == DeviceType.Controller)
{
assigner = new GamepadButtonAssigner(ViewModel.SelectedGamepad, (ViewModel.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(
LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmMessage],
LocaleManager.Instance[LocaleKeys.DialogControllerSettingsModifiedConfirmSubMessage],
LocaleManager.Instance[LocaleKeys.InputDialogYes],
LocaleManager.Instance[LocaleKeys.InputDialogNo],
LocaleManager.Instance[LocaleKeys.RyujinxConfirm]);
if (result == UserResult.Yes)
{
ViewModel.Save();
}
_dialogOpen = false;
ViewModel.IsModified = false;
if (e.AddedItems.Count > 0)
{
var player = (PlayerModel)e.AddedItems[0];
ViewModel.PlayerId = player.Id;
}
}
}
public void Dispose()
{
_currentAssigner?.Cancel();
_currentAssigner = null;
ViewModel.Dispose();
}
}
}

View File

@@ -1,141 +0,0 @@
<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: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"
Focusable="True">
<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

@@ -1,71 +0,0 @@
using Avalonia.Controls;
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 partial class MotionSettingsWindow : UserControl
{
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
public MotionSettingsWindow()
{
InitializeComponent();
DataContext = _viewmodel;
}
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();
DataContext = _viewmodel;
}
public static async Task Show(ControllerSettingsViewModel viewmodel)
{
MotionSettingsWindow content = new MotionSettingsWindow(viewmodel);
ContentDialog contentDialog = new ContentDialog
{
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
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

@@ -1,57 +0,0 @@
<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:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
mc:Ignorable="d"
x:Class="Ryujinx.Ava.UI.Windows.RumbleSettingsWindow"
Focusable="True">
<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

@@ -1,57 +0,0 @@
using Avalonia.Controls;
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 partial class RumbleSettingsWindow : UserControl
{
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
public RumbleSettingsWindow()
{
InitializeComponent();
DataContext = _viewmodel;
}
public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel)
{
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
{
StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble
};
InitializeComponent();
DataContext = _viewmodel;
}
public static async Task Show(ControllerSettingsViewModel viewmodel)
{
RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel);
ContentDialog contentDialog = new ContentDialog
{
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
SecondaryButtonText = "",
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
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();
}
}
}