Skip to content

Commit 3dcf914

Browse files
committed
Fixed actions manager
1 parent 2150127 commit 3dcf914

File tree

5 files changed

+343
-379
lines changed

5 files changed

+343
-379
lines changed

plugin_OpenVR/OpenVR.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ public static bool LogInputVerbose
8888

8989
private Driver.IIDriverService DriverService { get; set; }
9090
private Exception ServerDriverException { get; set; }
91-
private bool ServerDriverPresent => ServiceStatus == 0;
9291

9392
[Import(typeof(IAmethystHost))] public IAmethystHost Host { get; set; }
9493

@@ -292,7 +291,7 @@ 1 when VrHelper.IsCurrentProcessElevated() =>
292291
public Dictionary<TrackerType, SortedSet<IKeyInputAction>> SupportedInputActions =>
293292
Host is not null && (Host.IsTrackerEnabled(TrackerType.TrackerLeftHand) ||
294293
Host.IsTrackerEnabled(TrackerType.TrackerRightHand)) ?
295-
_supportedInputActions :
294+
(OperatingSystem.IsWindows() ? _supportedInputActions : []) :
296295
[];
297296

298297
public SortedSet<TrackerType> AdditionalSupportedTrackerTypes =>
@@ -310,7 +309,9 @@ 1 when VrHelper.IsCurrentProcessElevated() =>
310309
TrackerType.TrackerChest,
311310
TrackerType.TrackerCamera,
312311
TrackerType.TrackerKeyboard,
313-
TrackerType.TrackerHead,
312+
OperatingSystem.IsWindows() ?
313+
TrackerType.TrackerHead : // Emulation support
314+
TrackerType.TrackerWaist, // Disabled on linux
314315
TrackerType.TrackerLeftHand,
315316
TrackerType.TrackerRightHand
316317
];
@@ -1378,4 +1379,9 @@ public static Quaternion GetOrientation(this HmdMatrix34_t mat)
13781379
q.Z = MathF.CopySign(q.Z, mat.m4 - mat.m1);
13791380
return q; // Extracted, fixed ovr quaternion!
13801381
}
1382+
1383+
public static void ShowDialog(this IAmethystHost host, Control control)
1384+
{
1385+
host?.GetType()?.GetMethod("ShowDialog")?.Invoke(host, [control]);
1386+
}
13811387
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<UserControl xmlns="https://github.com/avaloniaui"
2+
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
3+
xmlns:utils="clr-namespace:plugin_OpenVR.Utils"
4+
xmlns:pluginOpenVr="clr-namespace:plugin_OpenVR"
5+
xmlns:controls="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
6+
x:Class="plugin_OpenVR.Pages.ActionsManager"
7+
MaxWidth="790" MinWidth="790" MaxHeight="475" MinHeight="475"
8+
x:Name="Root">
9+
10+
<!--Inner grid-->
11+
<Grid VerticalAlignment="Stretch" MaxWidth="790" MinWidth="790"
12+
MaxHeight="475" MinHeight="475" Margin="20"
13+
ColumnDefinitions="170,40,*" x:Name="OuterGrid">
14+
15+
<!--The selector part-->
16+
<Border Grid.Column="0" Margin="-20" Classes="cardSingle" Padding="30">
17+
<Grid RowDefinitions="*,Auto">
18+
<ScrollViewer HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
19+
VerticalScrollBarVisibility="Visible" Grid.Row="0"
20+
Padding="20,0, 13, 0" Margin="-20,0, -15, 0">
21+
<ListBox x:Name="ActionsListView"
22+
SelectionChanged="ActionsListView_OnSelectionChanged"
23+
ItemsSource="{Binding #Root.CustomActions, Mode=OneWay}">
24+
<ListBox.ItemTemplate>
25+
<DataTemplate x:DataType="pluginOpenVr:InputAction">
26+
<TextBlock Text="{Binding NameLocalized}" />
27+
</DataTemplate>
28+
</ListBox.ItemTemplate>
29+
</ListBox>
30+
</ScrollViewer>
31+
32+
<StackPanel Orientation="Vertical" Grid.Row="1">
33+
<Button Height="36" Click="NewActionItem_OnClick"
34+
BorderThickness="0" Background="Transparent">
35+
<Grid ColumnDefinitions="Auto, *" ColumnSpacing="6">
36+
<Viewbox MaxHeight="16" MaxWidth="16">
37+
<controls:SymbolIcon Symbol="Add" />
38+
</Viewbox>
39+
<TextBlock Text="{utils:Translate '/InputActions/Picker/Options/New'}"
40+
Grid.Column="1" VerticalAlignment="Center" FontSize="15" />
41+
</Grid>
42+
</Button>
43+
</StackPanel>
44+
</Grid>
45+
</Border>
46+
47+
<!--The preview part-->
48+
<Grid Grid.Column="2" RowDefinitions="Auto,Auto,*"
49+
Margin="-8,15,15,15" x:Name="PreviewGrid">
50+
<TextBlock FontWeight="SemiBold"
51+
Text="{Binding #Root.SelectedActionName, Mode=OneWay}"
52+
IsVisible="{Binding #Root.IsAddingNewActionInverse, Mode=OneWay}"
53+
VerticalAlignment="Top" HorizontalAlignment="Left" MaxWidth="370"
54+
FontSize="26" Margin="0,-7,0,0" TextTrimming="CharacterEllipsis" />
55+
56+
<TextBox FontWeight="SemiBold"
57+
Text="{Binding #Root.SelectedActionName, Mode=TwoWay}"
58+
IsVisible="{Binding #Root.IsAddingNewAction, Mode=OneWay}" Grid.Row="0"
59+
Watermark="{utils:Translate '/SettingsPage/Placeholders/NewAction'}"
60+
VerticalAlignment="Top" HorizontalAlignment="Left" FontSize="20"
61+
Margin="0,-5,0,5" Background="Transparent" MaxWidth="370" MinWidth="370"
62+
AcceptsReturn="False" TextWrapping="NoWrap" />
63+
64+
<SplitButton Click="ActionTestButton_OnClick" x:Name="ActionRemoveSplitButton"
65+
IsEnabled="{Binding #Root.ActionValid, Mode=OneWay}"
66+
IsVisible="{Binding #Root.IsAddingNewActionInverse, Mode=OneWay}"
67+
Content="{utils:Translate '/InputActions/Buttons/Test'}"
68+
HorizontalAlignment="Right" VerticalAlignment="Top" Grid.Row="0"
69+
Margin="0,-2,0,0">
70+
<SplitButton.Flyout>
71+
<Flyout Placement="Bottom">
72+
<Button Click="RemoveAction_OnClick"
73+
IsEnabled="{Binding #Root.ActionValid, Mode=OneWay}"
74+
Content="{utils:Translate '/InputActions/Buttons/Remove'}" />
75+
</Flyout>
76+
</SplitButton.Flyout>
77+
</SplitButton>
78+
79+
<Button Classes="accent" Click="AddNewAction_OnClick"
80+
IsEnabled="{Binding #Root.ActionValid, Mode=OneWay}"
81+
IsVisible="{Binding #Root.IsAddingNewAction, Mode=OneWay}"
82+
Content="{utils:Translate '/InputActions/Buttons/Add'}"
83+
HorizontalAlignment="Right" VerticalAlignment="Top" Grid.Row="0"
84+
Margin="0,-2,0,0" />
85+
86+
<!--"Select to preview" grid-->
87+
<Grid Grid.Row="2" Opacity="0.75"
88+
IsVisible="{Binding #Root.SelectedActionInvalid, Mode=OneWay}">
89+
<Grid.ColumnDefinitions>
90+
<ColumnDefinition Width="*" />
91+
<ColumnDefinition Width="8*" MinWidth="250" MaxWidth="500" />
92+
<ColumnDefinition Width="*" />
93+
</Grid.ColumnDefinitions>
94+
<StackPanel Grid.Column="1" Orientation="Vertical" VerticalAlignment="Center"
95+
HorizontalAlignment="Center" Margin="0,-35,0,0">
96+
<controls:FontIcon FontSize="120" Margin="0,0,0,25" Glyph="&#xE707;" />
97+
<TextBlock HorizontalAlignment="Center" TextWrapping="Wrap" FontSize="14"
98+
TextTrimming="CharacterEllipsis"
99+
Text="{utils:Translate '/InputActions/Picker/NoSelection'}" />
100+
</StackPanel>
101+
</Grid>
102+
103+
<!--Actual preview data-->
104+
<Grid Grid.Row="2" RowDefinitions="Auto, *, Auto" RowSpacing="12"
105+
IsVisible="{Binding #Root.SelectedActionValid, Mode=OneWay}"
106+
Opacity="{Binding #Root.SelectedActionValidOpacity, Mode=OneWay}">
107+
<Grid.Transitions>
108+
<Transitions>
109+
<DoubleTransition Property="Opacity" Duration="0:0:0.3"
110+
Easing="CircularEaseInOut" />
111+
</Transitions>
112+
</Grid.Transitions>
113+
114+
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" FontSize="14"
115+
Grid.Row="0" TextAlignment="Left" Margin="2,0,0,0"
116+
Text="{Binding #Root.SelectedActionDescription, Mode=OneWay}"
117+
Opacity="0.75" />
118+
<TextBox Grid.Row="1" Text="{Binding #Root.SelectedActionCode, Mode=TwoWay}"
119+
VerticalAlignment="Stretch" AcceptsReturn="True" TextWrapping="Wrap"
120+
Watermark="{utils:Translate '/InputActions/Code/Input'}" />
121+
<TextBox Grid.Row="2" x:Name="TestResultsBox" IsReadOnly="True"
122+
IsEnabled="{Binding #Root.IsAddingNewActionInverse, Mode=OneWay}"
123+
Text="{utils:Translate '/InputActions/Code/Test'}" />
124+
</Grid>
125+
</Grid>
126+
</Grid>
127+
</UserControl>
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Linq;
5+
using Amethyst.Contract;
6+
using Avalonia;
7+
using Avalonia.Controls;
8+
using Avalonia.Interactivity;
9+
using Avalonia.Markup.Xaml;
10+
11+
namespace plugin_OpenVR.Pages;
12+
13+
public partial class ActionsManager : UserControl, INotifyPropertyChanged
14+
{
15+
private bool _listViewChangeBlock = false;
16+
public new event PropertyChangedEventHandler PropertyChanged;
17+
18+
public bool IsAddingNewActionInverse
19+
{
20+
get => !IsAddingNewAction;
21+
}
22+
23+
public ActionsManager()
24+
{
25+
InitializeComponent();
26+
}
27+
28+
public IAmethystHost Host { get; set; }
29+
public SteamVR DataParent { get; set; }
30+
private InputAction TreeSelectedAction { get; set; }
31+
public bool IsAddingNewAction { get; set; }
32+
33+
public string SelectedActionName
34+
{
35+
get => IsAddingNewAction && TreeSelectedAction is not null ?
36+
NewActionName :
37+
TreeSelectedAction?.NameLocalized ?? Host?.RequestLocalizedString("/InputActions/Title/NoSelection") ?? "";
38+
set
39+
{
40+
if (!IsAddingNewAction || TreeSelectedAction is null) return;
41+
NewActionName = value;
42+
OnPropertyChanged();
43+
}
44+
}
45+
46+
public IEnumerable<InputAction> CustomActions
47+
{
48+
get => DataParent?.VrInput?.RegisteredActions?.Actions?.Where(x => x?.Custom ?? false) ?? [];
49+
}
50+
51+
public string SelectedActionCode
52+
{
53+
get => TreeSelectedAction?.Code ?? string.Empty;
54+
set
55+
{
56+
if (TreeSelectedAction is null) return;
57+
TreeSelectedAction.Code = value;
58+
}
59+
}
60+
61+
public string SelectedActionDescription
62+
{
63+
get => TreeSelectedAction?.Name ?? string.Empty;
64+
}
65+
66+
public bool SelectedActionValid
67+
{
68+
get => TreeSelectedAction?.Valid ?? false;
69+
}
70+
71+
public double SelectedActionValidOpacity
72+
{
73+
get => SelectedActionValid ? 1.0 : 0.0;
74+
}
75+
76+
public bool SelectedActionInvalid
77+
{
78+
get => !SelectedActionValid;
79+
}
80+
81+
public bool ActionValid
82+
{
83+
get => TreeSelectedAction is not null && (!IsAddingNewAction || !string.IsNullOrEmpty(SelectedActionName));
84+
}
85+
86+
public string NewActionName { get; set; }
87+
88+
private async void ActionTestButton_OnClick(object o, RoutedEventArgs routedEventArgs)
89+
{
90+
try
91+
{
92+
if (!TestResultsBox.IsLoaded || TreeSelectedAction is null) return;
93+
TestResultsBox.Text = await TreeSelectedAction.Invoke(null);
94+
}
95+
catch (Exception)
96+
{
97+
// ignored
98+
}
99+
}
100+
101+
private void RemoveAction_OnClick(object sender, RoutedEventArgs e)
102+
{
103+
if (!TestResultsBox.IsLoaded || TreeSelectedAction is null) return;
104+
DataParent.VrInput.RegisteredActions.Actions.Remove(TreeSelectedAction);
105+
DataParent.VrInput.SaveSettings();
106+
107+
TreeSelectedAction = null;
108+
ActionRemoveSplitButton?.Flyout?.Hide();
109+
110+
OnPropertyChanged();
111+
}
112+
113+
private void ActionsListView_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
114+
{
115+
if (sender is not ListBox || _listViewChangeBlock) return;
116+
if (e.AddedItems.Count < 1 || e.AddedItems[0] is not InputAction action)
117+
{
118+
Host?.PlayAppSound(SoundType.Focus);
119+
return; // Give up now...
120+
}
121+
122+
var shouldAnimate = TreeSelectedAction != action;
123+
TreeSelectedAction = action;
124+
IsAddingNewAction = false;
125+
OnPropertyChanged();
126+
127+
if (!shouldAnimate) return;
128+
Host?.PlayAppSound(SoundType.Invoke);
129+
}
130+
131+
private void NewActionItem_OnClick(object sender, RoutedEventArgs e)
132+
{
133+
if (!ActionsListView.IsLoaded) return;
134+
ActionsListView.SelectedItem = null;
135+
136+
TreeSelectedAction = new InputAction(
137+
$"/actions/default/in/{Guid.NewGuid().ToString().ToUpper()}",
138+
"boolean", "optional");
139+
140+
NewActionName = string.Empty;
141+
IsAddingNewAction = true;
142+
143+
Host?.PlayAppSound(SoundType.Invoke);
144+
OnPropertyChanged();
145+
}
146+
147+
private void ReloadActions()
148+
{
149+
TreeSelectedAction = null;
150+
IsAddingNewAction = !CustomActions.Any();
151+
if (IsAddingNewAction)
152+
{
153+
TreeSelectedAction = new InputAction(
154+
$"/actions/default/in/{Guid.NewGuid().ToString().ToUpper()}",
155+
"boolean", "optional");
156+
157+
NewActionName = string.Empty;
158+
}
159+
160+
ActionsListView.SelectedItem = null;
161+
OnPropertyChanged();
162+
}
163+
164+
private void OnPropertyChanged(string propertyName = null)
165+
{
166+
_listViewChangeBlock = true;
167+
var itemBackup = ActionsListView.SelectedItem;
168+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
169+
170+
if (ActionsListView.Items.Contains(itemBackup))
171+
ActionsListView.SelectedItem = itemBackup;
172+
173+
_listViewChangeBlock = false;
174+
}
175+
176+
private void AddNewAction_OnClick(object sender, RoutedEventArgs e)
177+
{
178+
if (!((sender as Button)?.IsLoaded ?? false)) return;
179+
180+
DataParent.VrInput.RegisteredActions.Actions.Add(TreeSelectedAction);
181+
TreeSelectedAction.NameLocalized = NewActionName;
182+
DataParent.VrInput.SaveSettings();
183+
DataParent.VrInput.InitInputActions();
184+
185+
IsAddingNewAction = false;
186+
Host?.PlayAppSound(SoundType.Invoke);
187+
188+
ReloadActions();
189+
190+
if (ActionsListView.Items.Any())
191+
ActionsListView.SelectedItem = ActionsListView.Items[^1];
192+
}
193+
194+
}

0 commit comments

Comments
 (0)