mirror of
https://github.com/hawkeye-stan/msfs-popout-panel-manager.git
synced 2024-11-21 13:20:11 +00:00
Continue floating panel development
This commit is contained in:
parent
025246d6fa
commit
daa1ec5978
14 changed files with 629 additions and 172 deletions
|
@ -1,4 +1,5 @@
|
||||||
using MSFSPopoutPanelManager.Shared;
|
using MSFSPopoutPanelManager.Shared;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace MSFSPopoutPanelManager.DomainModel.Profile
|
namespace MSFSPopoutPanelManager.DomainModel.Profile
|
||||||
{
|
{
|
||||||
|
@ -6,6 +7,9 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
|
||||||
{
|
{
|
||||||
public bool IsEnabled { get; set; }
|
public bool IsEnabled { get; set; }
|
||||||
|
|
||||||
public string KeyBinding { get; set; }
|
public string Binding { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool IsDetectingKeystroke { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,19 +18,22 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
|
||||||
|
|
||||||
private void PanelConfig_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
private void PanelConfig_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(FullScreen) && FullScreen)
|
var arg = e as PropertyChangedExtendedEventArgs;
|
||||||
|
|
||||||
|
if (arg.PropertyName == nameof(FullScreen) && FullScreen)
|
||||||
{
|
{
|
||||||
AlwaysOnTop = false;
|
AlwaysOnTop = false;
|
||||||
HideTitlebar = false;
|
HideTitlebar = false;
|
||||||
}
|
}
|
||||||
else if (e.PropertyName == nameof(TouchEnabled) && TouchEnabled)
|
else if (arg.PropertyName == nameof(TouchEnabled) && TouchEnabled)
|
||||||
{
|
{
|
||||||
AutoGameRefocus = true;
|
AutoGameRefocus = true;
|
||||||
}
|
}
|
||||||
else if (e.PropertyName == nameof(FloatingPanel))
|
else if (arg.ObjectName == QualifyFullName.Of(nameof(MSFSPopoutPanelManager.DomainModel.Profile.FloatingPanel)) &&
|
||||||
|
arg.PropertyName == nameof(FloatingPanel.IsEnabled))
|
||||||
{
|
{
|
||||||
if(!FloatingPanel.IsEnabled)
|
if (!FloatingPanel.IsEnabled)
|
||||||
FloatingPanel.KeyBinding = null;
|
FloatingPanel.Binding = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,8 +46,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
OnPanelConfigChanged();
|
OnPanelConfigChanged();
|
||||||
};
|
};
|
||||||
OnProfileChanged?.Invoke(this, EventArgs.Empty);
|
OnProfileChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
|
@ -55,9 +55,9 @@ namespace MSFSPopoutPanelManager.MainApp
|
||||||
services.AddSingleton(s => new ProfileOrchestrator(SharedStorage));
|
services.AddSingleton(s => new ProfileOrchestrator(SharedStorage));
|
||||||
services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
|
services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
|
||||||
services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
|
services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
|
||||||
services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>()));
|
services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService<FlightSimOrchestrator>(), s.GetRequiredService<KeyboardOrchestrator>()));
|
||||||
services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage));
|
services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage));
|
||||||
services.AddSingleton(s => new KeyboardOrchestrator(SharedStorage, s.GetRequiredService<PanelPopOutOrchestrator>(), s.GetRequiredService<PanelConfigurationOrchestrator>()));
|
services.AddSingleton(s => new KeyboardOrchestrator(SharedStorage));
|
||||||
services.AddSingleton(s => new HelpOrchestrator());
|
services.AddSingleton(s => new HelpOrchestrator());
|
||||||
|
|
||||||
services.AddSingleton(s => new OrchestratorUiHelper(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
|
services.AddSingleton(s => new OrchestratorUiHelper(SharedStorage, s.GetRequiredService<PanelSourceOrchestrator>(), s.GetRequiredService<PanelPopOutOrchestrator>()));
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
|
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
|
||||||
|
xmlns:converter="clr-namespace:MSFSPopoutPanelManager.MainApp.Converter"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<system:Double x:Key="IconSize">22</system:Double>
|
<system:Double x:Key="IconSize">22</system:Double>
|
||||||
<system:Double x:Key="ButtonSize">28</system:Double>
|
<system:Double x:Key="ButtonSize">28</system:Double>
|
||||||
|
<converter:KeystrokeBindingsConverter x:Key="KeystrokeBindingsConverter" />
|
||||||
<DataTrigger
|
<DataTrigger
|
||||||
x:Key="TriggerIsProfileLocked"
|
x:Key="TriggerIsProfileLocked"
|
||||||
Binding="{Binding ActiveProfile.IsLocked}"
|
Binding="{Binding ActiveProfile.IsLocked}"
|
||||||
|
@ -54,7 +56,7 @@
|
||||||
TargetType="TextBlock">
|
TargetType="TextBlock">
|
||||||
<Setter Property="FontSize" Value="13" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="TextWrapping" Value="Wrap" />
|
<Setter Property="TextWrapping" Value="Wrap" />
|
||||||
<Setter Property="Margin" Value="5,0,13,0" />
|
<Setter Property="Margin" Value="5,0,40,0" />
|
||||||
<Setter Property="LineHeight" Value="18" />
|
<Setter Property="LineHeight" Value="18" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style BasedOn="{StaticResource MaterialDesignIconForegroundButton}" TargetType="Button" />
|
<Style BasedOn="{StaticResource MaterialDesignIconForegroundButton}" TargetType="Button" />
|
||||||
|
@ -283,13 +285,13 @@
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander.Header>
|
</Expander.Header>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Margin="42,8,24,16"
|
Margin="46,8,24,16"
|
||||||
Orientation="Vertical"
|
Orientation="Vertical"
|
||||||
TextBlock.Foreground="{DynamicResource MaterialDesignBody}">
|
TextBlock.Foreground="{DynamicResource MaterialDesignBody}">
|
||||||
<WrapPanel Orientation="Horizontal">
|
<WrapPanel Orientation="Horizontal">
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
x:Name="TglBtnAlwaysOnTop"
|
x:Name="TglBtnAlwaysOnTop"
|
||||||
Margin="0,0,0,0"
|
Margin="5,0,0,0"
|
||||||
IsChecked="{Binding DataItem.AlwaysOnTop, Mode=TwoWay, NotifyOnSourceUpdated=True}"
|
IsChecked="{Binding DataItem.AlwaysOnTop, Mode=TwoWay, NotifyOnSourceUpdated=True}"
|
||||||
IsEnabled="{c:Binding !DataItem.FullScreen}"
|
IsEnabled="{c:Binding !DataItem.FullScreen}"
|
||||||
SourceUpdated="Data_SourceUpdated"
|
SourceUpdated="Data_SourceUpdated"
|
||||||
|
@ -321,30 +323,73 @@
|
||||||
SourceUpdated="Data_SourceUpdated"
|
SourceUpdated="Data_SourceUpdated"
|
||||||
Style="{StaticResource ToggleButton}" />
|
Style="{StaticResource ToggleButton}" />
|
||||||
<TextBlock Style="{StaticResource TextBlockLabel}" ToolTip="Automatic game refocus when clicking this panel or when using touch on this panel">Auto Game Refocus</TextBlock>
|
<TextBlock Style="{StaticResource TextBlockLabel}" ToolTip="Automatic game refocus when clicking this panel or when using touch on this panel">Auto Game Refocus</TextBlock>
|
||||||
|
</WrapPanel>
|
||||||
|
<WrapPanel Margin="0,10,0,0" VerticalAlignment="Center">
|
||||||
|
<Border
|
||||||
|
BorderBrush="Gray"
|
||||||
|
BorderThickness="1"
|
||||||
|
CornerRadius="4">
|
||||||
|
<WrapPanel Margin="4">
|
||||||
|
<ToggleButton
|
||||||
|
x:Name="TglBtnAllowFloatPanel"
|
||||||
|
Margin="0,4,0,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
IsChecked="{Binding DataItem.FloatingPanel.IsEnabled, Mode=TwoWay, NotifyOnSourceUpdated=True}"
|
||||||
|
SourceUpdated="Data_SourceUpdated"
|
||||||
|
Style="{StaticResource ToggleButton}" />
|
||||||
|
<TextBlock
|
||||||
|
Margin="5,4,10,4"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource TextBlockLabel}"
|
||||||
|
ToolTip="Set the pop out panel that can float above other windows">
|
||||||
|
Floating Window
|
||||||
|
</TextBlock>
|
||||||
|
<WrapPanel
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Visibility="{c:Binding DataItem.FloatingPanel.IsEnabled}">
|
||||||
|
|
||||||
<ToggleButton
|
<TextBlock
|
||||||
x:Name="TglBtnAllowFloatPanel"
|
Margin="5,4,10,4"
|
||||||
Margin="0"
|
VerticalAlignment="Center"
|
||||||
IsChecked="{Binding DataItem.FloatingPanel.IsEnabled, Mode=TwoWay, NotifyOnSourceUpdated=True}"
|
Style="{StaticResource TextBlockLabel}"
|
||||||
SourceUpdated="Data_SourceUpdated"
|
Text="Key Binding:" />
|
||||||
Style="{StaticResource ToggleButton}" />
|
<TextBlock
|
||||||
<TextBlock
|
x:Name="TextBlockFloatPanelKeyBinding"
|
||||||
Margin="5,0,10,0"
|
Margin="5,4,10,4"
|
||||||
Style="{StaticResource TextBlockLabel}"
|
VerticalAlignment="Center"
|
||||||
ToolTip="Set the pop out panel that can float above other windows">
|
Style="{StaticResource TextBlockLabel}"
|
||||||
Floating Window
|
Text="{Binding DataItem.FloatingPanel.Binding, Converter={StaticResource KeystrokeBindingsConverter}, ConverterParameter=' + '}"
|
||||||
</TextBlock>
|
Visibility="{c:Binding '!DataItem.FloatingPanel.IsDetectingKeystroke'}" />
|
||||||
<ComboBox
|
<TextBlock
|
||||||
x:Name="ComboBoxFloatPanelKeyBinding"
|
x:Name="TextBlockDetectingFloatPanelKeyBinding"
|
||||||
Width="60"
|
Margin="5,4,10,4"
|
||||||
Margin="0"
|
VerticalAlignment="Center"
|
||||||
Padding="0"
|
Style="{StaticResource TextBlockLabel}"
|
||||||
VerticalAlignment="top"
|
Text="Detecting..."
|
||||||
Loaded="ComboBoxFloatPanelKeyBinding_OnLoaded"
|
Visibility="{c:Binding 'DataItem.FloatingPanel.IsDetectingKeystroke'}" />
|
||||||
PreviewMouseLeftButtonDown="ComboBoxFloatPanelKeyBinding_OnPreviewMouseLeftButtonDown"
|
<Button
|
||||||
SelectionChanged="ComboBoxFloatPanelKeyBinding_OnSelectionChanged"
|
x:Name="BtnDetectFloatPanelKeyBinding"
|
||||||
ToolTip="Key binding to toggle floating pop out on and off"
|
Width="80"
|
||||||
Visibility="{c:Binding DataItem.FloatingPanel.IsEnabled}" />
|
Height="25"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding DetectFloatPanelKeyBindingCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignOutlinedButton}">
|
||||||
|
Detect
|
||||||
|
</Button>
|
||||||
|
<!--<Button
|
||||||
|
x:Name="BtnTest"
|
||||||
|
Width="80"
|
||||||
|
Height="25"
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Style="{StaticResource MaterialDesignOutlinedButton}">
|
||||||
|
Test
|
||||||
|
</Button>-->
|
||||||
|
</WrapPanel>
|
||||||
|
</WrapPanel>
|
||||||
|
</Border>
|
||||||
</WrapPanel>
|
</WrapPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Expander>
|
</Expander>
|
||||||
|
|
|
@ -2,9 +2,11 @@
|
||||||
using MSFSPopoutPanelManager.DomainModel.Profile;
|
using MSFSPopoutPanelManager.DomainModel.Profile;
|
||||||
using MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard;
|
using MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard;
|
||||||
using MSFSPopoutPanelManager.MainApp.ViewModel;
|
using MSFSPopoutPanelManager.MainApp.ViewModel;
|
||||||
|
using MSFSPopoutPanelManager.Orchestration;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
@ -69,74 +71,167 @@ namespace MSFSPopoutPanelManager.MainApp.AppUserControl
|
||||||
if (!string.IsNullOrEmpty(param))
|
if (!string.IsNullOrEmpty(param))
|
||||||
_viewModel.PanelAttributeUpdatedCommand.Execute(param);
|
_viewModel.PanelAttributeUpdatedCommand.Execute(param);
|
||||||
|
|
||||||
if (sender is ToggleButton { Name: "TglBtnAllowFloatPanel" })
|
//if (sender is ToggleButton { Name: "TglBtnAllowFloatPanel" })
|
||||||
ComboBoxFloatPanelKeyBinding.SelectedIndex = 0;
|
// ComboBoxFloatPanelKeyBinding.SelectedIndex = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComboBoxFloatPanelKeyBinding_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
//private void ComboBoxFloatPanelKeyBinding_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
{
|
//{
|
||||||
var comboBox = (ComboBox)sender;
|
// var comboBox = (ComboBox)sender;
|
||||||
|
|
||||||
if (comboBox.SelectedIndex is 0 or -1)
|
// if (comboBox.SelectedIndex is 0 or -1)
|
||||||
{
|
// {
|
||||||
_viewModel.DataItem.FloatingPanel.KeyBinding = null;
|
// _viewModel.DataItem.FloatingPanel.Binding = null;
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
var selectedValue = comboBox.SelectedValue.ToString();
|
// var selectedValue = comboBox.SelectedValue.ToString();
|
||||||
|
|
||||||
if (_viewModel.ActiveProfile.PanelConfigs.Any(x => x.FloatingPanel.KeyBinding == selectedValue && x.Id != _viewModel.DataItem.Id))
|
// if (_viewModel.ActiveProfile.PanelConfigs.Any(x => x.FloatingPanel.Binding == selectedValue && x.Id != _viewModel.DataItem.Id))
|
||||||
comboBox.SelectedIndex = 0;
|
// comboBox.SelectedIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
_viewModel.DataItem.FloatingPanel.KeyBinding = selectedValue;
|
// _viewModel.DataItem.FloatingPanel.Binding = selectedValue;
|
||||||
}
|
//}
|
||||||
|
|
||||||
private readonly List<string> _floatKeyBindings = new()
|
//private readonly List<string> _floatKeyBindings = new()
|
||||||
{
|
//{
|
||||||
"",
|
// "",
|
||||||
"Ctrl-1",
|
// "Ctrl-1",
|
||||||
"Ctrl-2",
|
// "Ctrl-2",
|
||||||
"Ctrl-3",
|
// "Ctrl-3",
|
||||||
"Ctrl-4",
|
// "Ctrl-4",
|
||||||
"Ctrl-5",
|
// "Ctrl-5",
|
||||||
"Ctrl-6",
|
// "Ctrl-6",
|
||||||
"Ctrl-7",
|
// "Ctrl-7",
|
||||||
"Ctrl-8",
|
// "Ctrl-8",
|
||||||
"Ctrl-9",
|
// "Ctrl-9",
|
||||||
"Ctrl-0"
|
// "Ctrl-0"
|
||||||
};
|
//};
|
||||||
|
|
||||||
private void ComboBoxFloatPanelKeyBinding_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
//private void ComboBoxFloatPanelKeyBinding_OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
//{
|
||||||
|
// BindFloatPanelKeyBinding();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private void ComboBoxFloatPanelKeyBinding_OnLoaded(object sender, RoutedEventArgs e)
|
||||||
|
//{
|
||||||
|
// BindFloatPanelKeyBinding();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private void BindFloatPanelKeyBinding()
|
||||||
|
//{
|
||||||
|
// var items = new List<string>();
|
||||||
|
// items.AddRange(_floatKeyBindings);
|
||||||
|
|
||||||
|
// foreach (var panelConfig in _viewModel.ActiveProfile.PanelConfigs)
|
||||||
|
// {
|
||||||
|
// if (panelConfig.FloatingPanel.Binding != null && panelConfig.Id != _viewModel.DataItem.Id)
|
||||||
|
// items.Remove(panelConfig.FloatingPanel.Binding);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// ComboBoxFloatPanelKeyBinding.ItemsSource = items;
|
||||||
|
|
||||||
|
// var index = items.ToList().FindIndex(x => string.Equals(x, _viewModel.DataItem.FloatingPanel.Binding, StringComparison.CurrentCultureIgnoreCase));
|
||||||
|
|
||||||
|
// if (index == -1)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// this.ComboBoxFloatPanelKeyBinding.SelectedIndex = index;
|
||||||
|
//}
|
||||||
|
|
||||||
|
private void BtnDetectFloatPanelKeyBinding_OnClick(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
BindFloatPanelKeyBinding();
|
this.KeyDown -= OnDetectKeyDown;
|
||||||
|
this.KeyDown += OnDetectKeyDown;
|
||||||
|
this.KeyUp -= OnDetectKeyUp;
|
||||||
|
this.KeyUp += OnDetectKeyUp;
|
||||||
|
|
||||||
|
_viewModel.DataItem.FloatingPanel.IsDetectingKeystroke = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ComboBoxFloatPanelKeyBinding_OnLoaded(object sender, RoutedEventArgs e)
|
private bool _isDetectingKeys;
|
||||||
|
|
||||||
|
private void OnDetectKeyDown(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
BindFloatPanelKeyBinding();
|
//if (!_isDetectingKeys)
|
||||||
|
//{
|
||||||
|
// _isDetectingKeys = true;
|
||||||
|
// _viewModel.DataItem.FloatingPanel.Binding.Clear();
|
||||||
|
//}
|
||||||
|
|
||||||
|
//_viewModel.DataItem.FloatingPanel.Binding.Add(e.Key == Key.System ? e.SystemKey.ToString() : e.Key.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void BindFloatPanelKeyBinding()
|
private void OnDetectKeyUp(object sender, KeyEventArgs e)
|
||||||
{
|
{
|
||||||
var items = new List<string>();
|
//_isDetectingKeys = false;
|
||||||
items.AddRange(_floatKeyBindings);
|
//Debug.WriteLine(string.Join(" + ", _viewModel.DataItem.FloatingPanel.Binding.ToArray()));
|
||||||
|
|
||||||
foreach (var panelConfig in _viewModel.ActiveProfile.PanelConfigs)
|
//this.KeyDown -= OnDetectKeyDown;
|
||||||
{
|
//this.KeyUp -= OnDetectKeyUp;
|
||||||
if (panelConfig.FloatingPanel.KeyBinding != null && panelConfig.Id != _viewModel.DataItem.Id)
|
|
||||||
items.Remove(panelConfig.FloatingPanel.KeyBinding);
|
|
||||||
}
|
|
||||||
|
|
||||||
ComboBoxFloatPanelKeyBinding.ItemsSource = items;
|
//_viewModel.DataItem.FloatingPanel.IsDetectingKeystroke = false;
|
||||||
|
|
||||||
var index = items.ToList().FindIndex(x => string.Equals(x, _viewModel.DataItem.FloatingPanel.KeyBinding, StringComparison.CurrentCultureIgnoreCase));
|
|
||||||
|
|
||||||
if (index == -1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.ComboBoxFloatPanelKeyBinding.SelectedIndex = index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//private List<KeyPressState> _capturesList = new();
|
||||||
|
//private bool _isCapturing;
|
||||||
|
|
||||||
|
//private GlobalKeyboardHook _globalKeyboardHook;
|
||||||
|
|
||||||
|
//private void BtnTest_OnClick(object sender, RoutedEventArgs e)
|
||||||
|
//{
|
||||||
|
// if (_globalKeyboardHook != null)
|
||||||
|
// {
|
||||||
|
// _globalKeyboardHook.KeyboardPressed -= GlobalKeyboardHookOnKeyboardPressed;
|
||||||
|
// _globalKeyboardHook?.Dispose();
|
||||||
|
// _globalKeyboardHook = null;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// _globalKeyboardHook = new GlobalKeyboardHook();
|
||||||
|
// _globalKeyboardHook.KeyboardPressed += GlobalKeyboardHookOnKeyboardPressed;
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//private void GlobalKeyboardHookOnKeyboardPressed(object sender, GlobalKeyboardHookEventArgs e)
|
||||||
|
//{
|
||||||
|
// if (e.KeyboardState == GlobalKeyboardHook.KeyboardState.KeyUp)
|
||||||
|
// {
|
||||||
|
// _isCapturing = false;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (e.KeyboardState is GlobalKeyboardHook.KeyboardState.KeyDown or GlobalKeyboardHook.KeyboardState.SysKeyDown && !_isCapturing)
|
||||||
|
// {
|
||||||
|
// _capturesList.Add(new KeyPressState { Key = e.KeyboardData.Key.ToString()});
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (e.KeyboardState is GlobalKeyboardHook.KeyboardState.KeyUp or GlobalKeyboardHook.KeyboardState.SysKeyUp && !_isCapturing)
|
||||||
|
// {
|
||||||
|
// var matched = _capturesList.FirstOrDefault(x => x.Key == e.KeyboardData.Key.ToString());
|
||||||
|
|
||||||
|
// if (matched == null)
|
||||||
|
// {
|
||||||
|
// // Error since there is keydown but no keyup
|
||||||
|
// _isCapturing = true;
|
||||||
|
// _capturesList.Clear();
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// matched.IsPressed = true;
|
||||||
|
|
||||||
|
// // Check if all keydown matches keyup
|
||||||
|
// if (_capturesList.All(x => x.IsPressed))
|
||||||
|
// {
|
||||||
|
// Debug.WriteLine(string.Join(" + ", _capturesList.ToArray().Select(x => x.Key)));
|
||||||
|
// _capturesList.Clear();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
MainApp/Converter/KeystrokeBindingsConverter.cs
Normal file
27
MainApp/Converter/KeystrokeBindingsConverter.cs
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace MSFSPopoutPanelManager.MainApp.Converter
|
||||||
|
{
|
||||||
|
public class KeystrokeBindingsConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value == null || value.GetType() != typeof(string))
|
||||||
|
return "Not Assigned";
|
||||||
|
|
||||||
|
var joinParam = parameter == null ? string.Empty : parameter.ToString();
|
||||||
|
|
||||||
|
var items = value.ToString().Split("|");
|
||||||
|
|
||||||
|
return string.Join(joinParam, items.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(
|
||||||
|
object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,5 @@ namespace MSFSPopoutPanelManager.MainApp.Converter
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,8 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
|
||||||
|
|
||||||
public ICommand MoveResizePanelCommand { get; set; }
|
public ICommand MoveResizePanelCommand { get; set; }
|
||||||
|
|
||||||
|
public ICommand DetectFloatPanelKeyBindingCommand { get; set; }
|
||||||
|
|
||||||
public DelegateCommand<string> PanelAttributeUpdatedCommand { get; set; }
|
public DelegateCommand<string> PanelAttributeUpdatedCommand { get; set; }
|
||||||
|
|
||||||
public PopOutPanelConfigCardViewModel(SharedStorage sharedStorage, PanelSourceOrchestrator panelSourceOrchestrator, PanelConfigurationOrchestrator panelConfigurationOrchestrator) : base(sharedStorage)
|
public PopOutPanelConfigCardViewModel(SharedStorage sharedStorage, PanelSourceOrchestrator panelSourceOrchestrator, PanelConfigurationOrchestrator panelConfigurationOrchestrator) : base(sharedStorage)
|
||||||
|
@ -64,7 +66,11 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
|
||||||
.ObservesProperty(() => ActiveProfile);
|
.ObservesProperty(() => ActiveProfile);
|
||||||
|
|
||||||
DeletePanelCommand = new DelegateCommand(OnDeletePanel);
|
DeletePanelCommand = new DelegateCommand(OnDeletePanel);
|
||||||
|
|
||||||
|
|
||||||
PanelAttributeUpdatedCommand = new DelegateCommand<string>(OnPanelAttributeUpdated);
|
PanelAttributeUpdatedCommand = new DelegateCommand<string>(OnPanelAttributeUpdated);
|
||||||
|
|
||||||
|
DetectFloatPanelKeyBindingCommand = new DelegateCommand(OnDetectFloatPanelKeyBinding);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMovePanelUp()
|
private void OnMovePanelUp()
|
||||||
|
@ -205,5 +211,10 @@ namespace MSFSPopoutPanelManager.MainApp.ViewModel
|
||||||
if (panelConfigPropertyName != PanelConfigPropertyName.None)
|
if (panelConfigPropertyName != PanelConfigPropertyName.None)
|
||||||
_panelConfigurationOrchestrator.PanelConfigPropertyUpdated(DataItem.PanelHandle, panelConfigPropertyName);
|
_panelConfigurationOrchestrator.PanelConfigPropertyUpdated(DataItem.PanelHandle, panelConfigPropertyName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnDetectFloatPanelKeyBinding()
|
||||||
|
{
|
||||||
|
_panelConfigurationOrchestrator.StartDetectKeystroke(DataItem.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
|
|
||||||
ProfileData.SetActiveProfile(AppSettingData.ApplicationSetting.SystemSetting.LastUsedProfileId); // Load last used profile
|
ProfileData.SetActiveProfile(AppSettingData.ApplicationSetting.SystemSetting.LastUsedProfileId); // Load last used profile
|
||||||
|
|
||||||
Task.Run(() => _flightSimOrchestrator.StartSimConnectServer()); // Start the SimConnect server
|
Task.Run(() => _flightSimOrchestrator.StartSimConnectServer()); // Start the SimConnect server
|
||||||
|
|
||||||
_keyboardOrchestrator.Initialize();
|
_keyboardOrchestrator.Initialize();
|
||||||
}
|
}
|
||||||
|
|
208
Orchestration/GlobalKeyboardHook.cs
Normal file
208
Orchestration/GlobalKeyboardHook.cs
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace MSFSPopoutPanelManager.Orchestration
|
||||||
|
{
|
||||||
|
public class GlobalKeyboardHookEventArgs : HandledEventArgs
|
||||||
|
{
|
||||||
|
public GlobalKeyboardHook.KeyboardState KeyboardState { get; private set; }
|
||||||
|
public GlobalKeyboardHook.LowLevelKeyboardInputEvent KeyboardData { get; private set; }
|
||||||
|
|
||||||
|
public GlobalKeyboardHookEventArgs(
|
||||||
|
GlobalKeyboardHook.LowLevelKeyboardInputEvent keyboardData,
|
||||||
|
GlobalKeyboardHook.KeyboardState keyboardState)
|
||||||
|
{
|
||||||
|
KeyboardData = keyboardData;
|
||||||
|
KeyboardState = keyboardState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GlobalKeyboardHook : IDisposable
|
||||||
|
{
|
||||||
|
public event EventHandler<GlobalKeyboardHookEventArgs> KeyboardPressed;
|
||||||
|
|
||||||
|
public GlobalKeyboardHook()
|
||||||
|
{
|
||||||
|
_windowsHookHandle = IntPtr.Zero;
|
||||||
|
_user32LibraryHandle = IntPtr.Zero;
|
||||||
|
_hookProc = LowLevelKeyboardProc; // we must keep alive _hookProc, because GC is not aware about SetWindowsHookEx behaviour.
|
||||||
|
|
||||||
|
_user32LibraryHandle = LoadLibrary("User32");
|
||||||
|
if (_user32LibraryHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
int errorCode = Marshal.GetLastWin32Error();
|
||||||
|
throw new Win32Exception(errorCode, $"Failed to load library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
_windowsHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, _user32LibraryHandle, 0);
|
||||||
|
if (_windowsHookHandle == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
int errorCode = Marshal.GetLastWin32Error();
|
||||||
|
throw new Win32Exception(errorCode, $"Failed to adjust keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
// because we can unhook only in the same thread, not in garbage collector thread
|
||||||
|
if (_windowsHookHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (!UnhookWindowsHookEx(_windowsHookHandle))
|
||||||
|
{
|
||||||
|
int errorCode = Marshal.GetLastWin32Error();
|
||||||
|
throw new Win32Exception(errorCode, $"Failed to remove keyboard hooks for '{Process.GetCurrentProcess().ProcessName}'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||||
|
}
|
||||||
|
_windowsHookHandle = IntPtr.Zero;
|
||||||
|
|
||||||
|
// ReSharper disable once DelegateSubtraction
|
||||||
|
_hookProc -= LowLevelKeyboardProc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_user32LibraryHandle != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
if (!FreeLibrary(_user32LibraryHandle)) // reduces reference to library by 1.
|
||||||
|
{
|
||||||
|
int errorCode = Marshal.GetLastWin32Error();
|
||||||
|
throw new Win32Exception(errorCode, $"Failed to unload library 'User32.dll'. Error {errorCode}: {new Win32Exception(Marshal.GetLastWin32Error()).Message}.");
|
||||||
|
}
|
||||||
|
_user32LibraryHandle = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~GlobalKeyboardHook()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr _windowsHookHandle;
|
||||||
|
private IntPtr _user32LibraryHandle;
|
||||||
|
private HookProc _hookProc;
|
||||||
|
|
||||||
|
delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll")]
|
||||||
|
private static extern IntPtr LoadLibrary(string lpFileName);
|
||||||
|
|
||||||
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
|
||||||
|
private static extern bool FreeLibrary(IntPtr hModule);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain.
|
||||||
|
/// You would install a hook procedure to monitor the system for certain types of events. These events are
|
||||||
|
/// associated either with a specific thread or with all threads in the same desktop as the calling thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="idHook">hook type</param>
|
||||||
|
/// <param name="lpfn">hook procedure</param>
|
||||||
|
/// <param name="hMod">handle to application instance</param>
|
||||||
|
/// <param name="dwThreadId">thread identifier</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is the handle to the hook procedure.</returns>
|
||||||
|
[DllImport("USER32", SetLastError = true)]
|
||||||
|
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, int dwThreadId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx function.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hhk">handle to hook procedure</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is true.</returns>
|
||||||
|
[DllImport("USER32", SetLastError = true)]
|
||||||
|
public static extern bool UnhookWindowsHookEx(IntPtr hHook);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain.
|
||||||
|
/// A hook procedure can call this function either before or after processing the hook information.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="hHook">handle to current hook</param>
|
||||||
|
/// <param name="code">hook code passed to hook procedure</param>
|
||||||
|
/// <param name="wParam">value passed to hook procedure</param>
|
||||||
|
/// <param name="lParam">value passed to hook procedure</param>
|
||||||
|
/// <returns>If the function succeeds, the return value is true.</returns>
|
||||||
|
[DllImport("USER32", SetLastError = true)]
|
||||||
|
static extern IntPtr CallNextHookEx(IntPtr hHook, int code, IntPtr wParam, IntPtr lParam);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct LowLevelKeyboardInputEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A virtual-key code. The code must be a value in the range 1 to 254.
|
||||||
|
/// </summary>
|
||||||
|
public int VirtualCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A hardware scan code for the key.
|
||||||
|
/// </summary>
|
||||||
|
public int HardwareScanCode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The extended-key flag, event-injected Flags, context code, and transition-state flag. This member is specified as follows. An application can use the following values to test the keystroke Flags. Testing LLKHF_INJECTED (bit 4) will tell you whether the event was injected. If it was, then testing LLKHF_LOWER_IL_INJECTED (bit 1) will tell you whether or not the event was injected from a process running at lower integrity level.
|
||||||
|
/// </summary>
|
||||||
|
public int Flags;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time stamp stamp for this message, equivalent to what GetMessageTime would return for this message.
|
||||||
|
/// </summary>
|
||||||
|
public int TimeStamp;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Additional information associated with the message.
|
||||||
|
/// </summary>
|
||||||
|
public IntPtr AdditionalInformation;
|
||||||
|
|
||||||
|
public Key Key => KeyInterop.KeyFromVirtualKey(VirtualCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int WH_KEYBOARD_LL = 13;
|
||||||
|
//const int HC_ACTION = 0;
|
||||||
|
|
||||||
|
public enum KeyboardState
|
||||||
|
{
|
||||||
|
KeyDown = 0x0100,
|
||||||
|
KeyUp = 0x0101,
|
||||||
|
SysKeyDown = 0x0104,
|
||||||
|
SysKeyUp = 0x0105
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int VkSnapshot = 0x2c;
|
||||||
|
//const int VkLwin = 0x5b;
|
||||||
|
//const int VkRwin = 0x5c;
|
||||||
|
//const int VkTab = 0x09;
|
||||||
|
//const int VkEscape = 0x18;
|
||||||
|
//const int VkControl = 0x11;
|
||||||
|
const int KfAltdown = 0x2000;
|
||||||
|
public const int LlkhfAltdown = (KfAltdown >> 8);
|
||||||
|
|
||||||
|
public IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
|
||||||
|
{
|
||||||
|
bool fEatKeyStroke = false;
|
||||||
|
|
||||||
|
var wparamTyped = wParam.ToInt32();
|
||||||
|
if (Enum.IsDefined(typeof(KeyboardState), wparamTyped))
|
||||||
|
{
|
||||||
|
object o = Marshal.PtrToStructure(lParam, typeof(LowLevelKeyboardInputEvent));
|
||||||
|
LowLevelKeyboardInputEvent p = (LowLevelKeyboardInputEvent)o;
|
||||||
|
|
||||||
|
var eventArguments = new GlobalKeyboardHookEventArgs(p, (KeyboardState)wparamTyped);
|
||||||
|
|
||||||
|
EventHandler<GlobalKeyboardHookEventArgs> handler = KeyboardPressed;
|
||||||
|
handler?.Invoke(this, eventArguments);
|
||||||
|
|
||||||
|
fEatKeyStroke = eventArguments.Handled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fEatKeyStroke ? (IntPtr)1 : CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,21 +1,20 @@
|
||||||
using System.Linq;
|
using MSFSPopoutPanelManager.WindowsAgent;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System;
|
||||||
using MSFSPopoutPanelManager.DomainModel.Profile;
|
using System.Collections.Generic;
|
||||||
using MSFSPopoutPanelManager.WindowsAgent;
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace MSFSPopoutPanelManager.Orchestration
|
namespace MSFSPopoutPanelManager.Orchestration
|
||||||
{
|
{
|
||||||
public class KeyboardOrchestrator : BaseOrchestrator
|
public class KeyboardOrchestrator : BaseOrchestrator
|
||||||
{
|
{
|
||||||
private readonly PanelPopOutOrchestrator _panelPopOutOrchestrator;
|
private GlobalKeyboardHook _globalKeyboardHook;
|
||||||
private readonly PanelConfigurationOrchestrator _panelConfigurationOrchestrator;
|
|
||||||
|
|
||||||
public KeyboardOrchestrator(SharedStorage sharedStorage, PanelPopOutOrchestrator panelPopOutOrchestrator, PanelConfigurationOrchestrator panelConfigurationOrchestrator) : base(sharedStorage)
|
|
||||||
|
public KeyboardOrchestrator(SharedStorage sharedStorage) : base(sharedStorage)
|
||||||
{
|
{
|
||||||
_panelPopOutOrchestrator = panelPopOutOrchestrator;
|
|
||||||
_panelConfigurationOrchestrator = panelConfigurationOrchestrator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
if (AppSettingData.ApplicationSetting.KeyboardShortcutSetting.IsEnabled)
|
if (AppSettingData.ApplicationSetting.KeyboardShortcutSetting.IsEnabled)
|
||||||
|
@ -40,73 +39,80 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (ProfileData.ActiveProfile.PanelConfigs.Any(x => x.FloatingPanel.IsEnabled))
|
|
||||||
{
|
|
||||||
InputHookManager.StartKeyboardHook("FloatingPanel");
|
|
||||||
InputHookManager.OnKeyUp -= HandleFloatingPanelKeyboardHookKeyUpEvent;
|
|
||||||
InputHookManager.OnKeyUp += HandleFloatingPanelKeyboardHookKeyUpEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ProfileData.ActiveProfile.OnUseFloatingPanelChanged += (_, e) =>
|
|
||||||
{
|
|
||||||
if (e)
|
|
||||||
{
|
|
||||||
InputHookManager.StartKeyboardHook("FloatingPanel");
|
|
||||||
InputHookManager.OnKeyUp -= HandleFloatingPanelKeyboardHookKeyUpEvent;
|
|
||||||
InputHookManager.OnKeyUp += HandleFloatingPanelKeyboardHookKeyUpEvent;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InputHookManager.EndKeyboardHook("FloatingPanel");
|
|
||||||
InputHookManager.OnKeyUp -= HandleFloatingPanelKeyboardHookKeyUpEvent;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void HandleShortcutKeyboardHookKeyUpEvent(object sender, KeyUpEventArgs e)
|
public async void HandleShortcutKeyboardHookKeyUpEvent(object sender, KeyUpEventArgs e)
|
||||||
{
|
{
|
||||||
// Start pop out
|
// Start pop out
|
||||||
if (e.IsHoldControl && e.IsHoldShift && e.KeyCode.ToUpper() ==
|
if (e.IsHoldControl && e.IsHoldShift && e.KeyCode.ToUpper() ==
|
||||||
AppSettingData.ApplicationSetting.KeyboardShortcutSetting.StartPopOutKeyBinding)
|
AppSettingData.ApplicationSetting.KeyboardShortcutSetting.StartPopOutKeyBinding)
|
||||||
{
|
{
|
||||||
await _panelPopOutOrchestrator.ManualPopOut();
|
//await _panelPopOutOrchestrator.ManualPopOut();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleFloatingPanelKeyboardHookKeyUpEvent(object sender, KeyUpEventArgs e)
|
|
||||||
|
private List<string> _keyPressCaptureList = new();
|
||||||
|
public event EventHandler<DetectKeystrokeEventArg> OnKeystrokeDetected;
|
||||||
|
private bool _isCapturingKeyPress;
|
||||||
|
private Guid? _panelId;
|
||||||
|
|
||||||
|
public void StartGlobalKeyboardHook(Guid? panelId = null)
|
||||||
{
|
{
|
||||||
if (e.IsHoldControl)
|
_isCapturingKeyPress = true;
|
||||||
|
_panelId = panelId;
|
||||||
|
|
||||||
|
if (_globalKeyboardHook != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.WriteLine("Starts Global Keyboard Hook");
|
||||||
|
_globalKeyboardHook ??= new GlobalKeyboardHook();
|
||||||
|
_globalKeyboardHook.KeyboardPressed -= HandleGlobalKeyboardHookOnKeyboardPressed;
|
||||||
|
_globalKeyboardHook.KeyboardPressed += HandleGlobalKeyboardHookOnKeyboardPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EndGlobalKeyboardHook()
|
||||||
|
{
|
||||||
|
if (_globalKeyboardHook == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Debug.WriteLine("Ends Global Keyboard Hook");
|
||||||
|
_keyPressCaptureList = new List<string>();
|
||||||
|
_globalKeyboardHook.KeyboardPressed -= HandleGlobalKeyboardHookOnKeyboardPressed;
|
||||||
|
_globalKeyboardHook?.Dispose();
|
||||||
|
_globalKeyboardHook = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void HandleGlobalKeyboardHookOnKeyboardPressed(object sender, GlobalKeyboardHookEventArgs e)
|
||||||
|
{
|
||||||
|
switch (e.KeyboardState)
|
||||||
{
|
{
|
||||||
switch (e.KeyCode)
|
case GlobalKeyboardHook.KeyboardState.KeyDown or GlobalKeyboardHook.KeyboardState.SysKeyDown:
|
||||||
|
_isCapturingKeyPress = true;
|
||||||
|
_keyPressCaptureList.Add(e.KeyboardData.Key.ToString());
|
||||||
|
break;
|
||||||
|
case GlobalKeyboardHook.KeyboardState.KeyUp or GlobalKeyboardHook.KeyboardState.SysKeyUp when _isCapturingKeyPress:
|
||||||
{
|
{
|
||||||
case "D1":
|
_isCapturingKeyPress = false;
|
||||||
case "D2":
|
|
||||||
case "D3":
|
var keyBinding = string.Join("|", _keyPressCaptureList.DistinctBy(x => x).OrderBy(x => x).ToArray().Select(x => x));
|
||||||
case "D4":
|
OnKeystrokeDetected?.Invoke(this, new DetectKeystrokeEventArg {PanelId = _panelId, KeyBinding = keyBinding});
|
||||||
case "D5":
|
|
||||||
case "D6":
|
_panelId = null;
|
||||||
case "D7":
|
_keyPressCaptureList.Clear();
|
||||||
case "D8":
|
break;
|
||||||
case "D9":
|
|
||||||
case "D0":
|
|
||||||
_panelConfigurationOrchestrator.ToggleFloatPanel($"Ctrl-{e.KeyCode[1..]}");
|
|
||||||
break;
|
|
||||||
case "NumPad1":
|
|
||||||
case "NumPad2":
|
|
||||||
case "NumPad3":
|
|
||||||
case "NumPad4":
|
|
||||||
case "NumPad5":
|
|
||||||
case "NumPad6":
|
|
||||||
case "NumPad7":
|
|
||||||
case "NumPad8":
|
|
||||||
case "NumPad9":
|
|
||||||
case "NumPad0":
|
|
||||||
_panelConfigurationOrchestrator.ToggleFloatPanel($"Ctrl-{e.KeyCode[6..]}");
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class DetectKeystrokeEventArg : EventArgs
|
||||||
|
{
|
||||||
|
public Guid? PanelId { get; set; }
|
||||||
|
|
||||||
|
public string KeyBinding { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,13 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
{
|
{
|
||||||
public class PanelConfigurationOrchestrator : BaseOrchestrator
|
public class PanelConfigurationOrchestrator : BaseOrchestrator
|
||||||
{
|
{
|
||||||
public PanelConfigurationOrchestrator(SharedStorage sharedStorage, FlightSimOrchestrator flightSimOrchestrator) : base(sharedStorage)
|
private UserProfile ActiveProfile => ProfileData?.ActiveProfile;
|
||||||
|
private readonly KeyboardOrchestrator _keyboardOrchestrator;
|
||||||
|
|
||||||
|
public PanelConfigurationOrchestrator(SharedStorage sharedStorage, FlightSimOrchestrator flightSimOrchestrator, KeyboardOrchestrator keyboardOrchestrator) : base(sharedStorage)
|
||||||
{
|
{
|
||||||
|
_keyboardOrchestrator = keyboardOrchestrator;
|
||||||
|
|
||||||
AppSettingData.OnEnablePanelResetWhenLockedChanged += (_, _) =>
|
AppSettingData.OnEnablePanelResetWhenLockedChanged += (_, _) =>
|
||||||
{
|
{
|
||||||
if (FlightSimData.IsInCockpit)
|
if (FlightSimData.IsInCockpit)
|
||||||
|
@ -24,9 +29,42 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
EndConfiguration();
|
EndConfiguration();
|
||||||
EndTouchHook();
|
EndTouchHook();
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
private UserProfile ActiveProfile => ProfileData?.ActiveProfile;
|
_keyboardOrchestrator.OnKeystrokeDetected += (_, e) =>
|
||||||
|
{
|
||||||
|
var panel = ActiveProfile.PanelConfigs.FirstOrDefault(p => p.Id == e.PanelId);
|
||||||
|
|
||||||
|
if (panel != null && panel.FloatingPanel.IsDetectingKeystroke)
|
||||||
|
{
|
||||||
|
panel.FloatingPanel.Binding = e.KeyBinding;
|
||||||
|
panel.FloatingPanel.IsDetectingKeystroke = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ToggleFloatPanel(e.KeyBinding);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ProfileData.OnUseFloatingPanelChanged += (_, e) =>
|
||||||
|
{
|
||||||
|
if (ActiveProfile == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (e)
|
||||||
|
_keyboardOrchestrator.StartGlobalKeyboardHook();
|
||||||
|
else
|
||||||
|
_keyboardOrchestrator.EndGlobalKeyboardHook();
|
||||||
|
};
|
||||||
|
|
||||||
|
ProfileData.OnActiveProfileChanged += (_, _) =>
|
||||||
|
{
|
||||||
|
if (ActiveProfile == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ActiveProfile.PanelConfigs.Any(x => x.FloatingPanel.IsEnabled))
|
||||||
|
_keyboardOrchestrator.StartGlobalKeyboardHook();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void StartConfiguration()
|
public void StartConfiguration()
|
||||||
{
|
{
|
||||||
|
@ -157,31 +195,50 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
|
|
||||||
public void ToggleFloatPanel(string keyBinding)
|
public void ToggleFloatPanel(string keyBinding)
|
||||||
{
|
{
|
||||||
var panel = ActiveProfile.PanelConfigs.FirstOrDefault(x => string.Equals(x.FloatingPanel.KeyBinding, keyBinding, StringComparison.CurrentCultureIgnoreCase));
|
var panels = ActiveProfile.PanelConfigs.ToList().FindAll(x => string.Equals(x.FloatingPanel.Binding, keyBinding, StringComparison.Ordinal));
|
||||||
|
|
||||||
if (panel == null)
|
if (!panels.Any())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!panel.FloatingPanel.IsEnabled || panel.FullScreen)
|
foreach (var panel in panels)
|
||||||
return;
|
|
||||||
|
|
||||||
if (panel.PanelType is not (PanelType.CustomPopout or PanelType.BuiltInPopout))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (panel.IsPopOutSuccess == null || !(bool)panel.IsPopOutSuccess)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!panel.IsFloating)
|
|
||||||
{
|
{
|
||||||
panel.IsFloating = true;
|
|
||||||
WindowActionManager.RestoreWindow(panel.PanelHandle);
|
if (!panel.FloatingPanel.IsEnabled || panel.FullScreen)
|
||||||
WindowActionManager.ApplyAlwaysOnTop(panel.PanelHandle, panel.PanelType, true);
|
return;
|
||||||
}
|
|
||||||
else
|
if (panel.PanelType is not (PanelType.CustomPopout or PanelType.BuiltInPopout))
|
||||||
{
|
return;
|
||||||
panel.IsFloating = false;
|
|
||||||
WindowActionManager.MinimizeWindow(panel.PanelHandle);
|
if (panel.IsPopOutSuccess == null || !(bool)panel.IsPopOutSuccess)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!panel.IsFloating)
|
||||||
|
{
|
||||||
|
panel.IsFloating = true;
|
||||||
|
WindowActionManager.RestoreWindow(panel.PanelHandle);
|
||||||
|
WindowActionManager.ApplyAlwaysOnTop(panel.PanelHandle, panel.PanelType, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
panel.IsFloating = false;
|
||||||
|
WindowActionManager.MinimizeWindow(panel.PanelHandle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void StartDetectKeystroke(Guid panelId)
|
||||||
|
{
|
||||||
|
var panel = ActiveProfile.PanelConfigs.FirstOrDefault(p => p.Id == panelId);
|
||||||
|
|
||||||
|
if (panel != null)
|
||||||
|
panel.FloatingPanel.IsDetectingKeystroke = true;
|
||||||
|
|
||||||
|
_keyboardOrchestrator.StartGlobalKeyboardHook(panelId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopDetectKeystroke(Guid panelId)
|
||||||
|
{
|
||||||
|
_keyboardOrchestrator.EndGlobalKeyboardHook();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
public class ProfileData : ObservableObject
|
public class ProfileData : ObservableObject
|
||||||
{
|
{
|
||||||
public event PropertyChangedEventHandler OnActiveProfileChanged;
|
public event PropertyChangedEventHandler OnActiveProfileChanged;
|
||||||
|
public event EventHandler<bool> OnUseFloatingPanelChanged;
|
||||||
|
|
||||||
public SortedObservableCollection<UserProfile> Profiles { get; private set; } = new();
|
public SortedObservableCollection<UserProfile> Profiles { get; private set; } = new();
|
||||||
|
|
||||||
|
@ -181,15 +182,18 @@ namespace MSFSPopoutPanelManager.Orchestration
|
||||||
|
|
||||||
Profiles.ToList().ForEach(p => p.IsActive = false);
|
Profiles.ToList().ForEach(p => p.IsActive = false);
|
||||||
|
|
||||||
if(ActiveProfile != null)
|
if (ActiveProfile != null)
|
||||||
|
{
|
||||||
ActiveProfile.IsActive = true;
|
ActiveProfile.IsActive = true;
|
||||||
|
ActiveProfile.OnUseFloatingPanelChanged += (sender, e) => OnUseFloatingPanelChanged?.Invoke(sender, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
OnActiveProfileChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetActiveProfile(int profileIndex)
|
public void SetActiveProfile(int profileIndex)
|
||||||
{
|
{
|
||||||
SetActiveProfile(profileIndex == -1 ? Guid.Empty : Profiles[profileIndex].Id);
|
SetActiveProfile(profileIndex == -1 ? Guid.Empty : Profiles[profileIndex].Id);
|
||||||
OnActiveProfileChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserProfile ActiveProfile { get; private set; }
|
public UserProfile ActiveProfile { get; private set; }
|
||||||
|
|
Loading…
Reference in a new issue