1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-11-25 07:10:10 +00:00

Merge remote-tracking branch 'origin/touch-enable-pop-out'

This commit is contained in:
hawkeye 2022-06-22 11:07:51 -04:00
commit 900fa7f3bc
13 changed files with 197 additions and 64 deletions

View file

@ -5,15 +5,15 @@
<AssemblyName>FsConnector</AssemblyName>
<PackageId>MSFS 2020 Popout Panel Manager FsConnector</PackageId>
<Product>MSFS 2020 Popout Panel Manager FsConnector</Product>
<Version>3.3.3.0</Version>
<Version>3.4.0.0</Version>
<Authors>Stanley Kwok</Authors>
<Company>Stanley Kwok</Company>
<Copyright>Stanley Kwok 2021</Copyright>
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<RootNamespace>MSFSPopoutPanelManager.FsConnector</RootNamespace>
<Platforms>x64;AnyCPU</Platforms>
<AssemblyVersion>3.3.3.0</AssemblyVersion>
<FileVersion>3.3.3.0</FileVersion>
<AssemblyVersion>3.4.0.0</AssemblyVersion>
<FileVersion>3.4.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View file

@ -5,15 +5,15 @@
<RootNamespace>MSFSPopoutPanelManager.Model</RootNamespace>
<AssemblyName>Model</AssemblyName>
<PackageId>MSFS 2020 Popout Panel Manager Model</PackageId>
<Version>3.3.3.0</Version>
<Version>3.4.0.0</Version>
<Authors>Stanley Kwok</Authors>
<Company>Stanley Kwok</Company>
<Copyright>Stanley Kwok 2021</Copyright>
<Product>MSFS 2020 Popout Panel Manager Model</Product>
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<Platforms>x64;AnyCPU</Platforms>
<AssemblyVersion>3.3.3.0</AssemblyVersion>
<FileVersion>3.3.3.0</FileVersion>
<AssemblyVersion>3.4.0.0</AssemblyVersion>
<FileVersion>3.4.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View file

@ -28,10 +28,45 @@ namespace MSFSPopoutPanelManager.Model
public bool FullScreen { get; set; }
public bool TouchEnabled { get; set; }
[JsonIgnore]
public bool IsCustomPopout { get { return PanelType == PanelType.CustomPopout; } }
[JsonIgnore]
public IntPtr PanelHandle { get; set; }
[JsonIgnore]
public bool IsLockable
{
get
{
switch (PanelType)
{
case PanelType.CustomPopout:
case PanelType.BuiltInPopout:
case PanelType.MSFSTouchPanel:
return true;
default:
return false;
}
}
}
[JsonIgnore]
public bool HasTouchableEvent
{
get
{
switch (PanelType)
{
case PanelType.CustomPopout:
case PanelType.BuiltInPopout:
return true;
default:
return false;
}
}
}
}
}

View file

@ -12,6 +12,7 @@ namespace MSFSPopoutPanelManager.Model
AlwaysOnTop,
HideTitlebar,
FullScreen,
TouchEnabled,
Invalid
}

View file

@ -28,6 +28,13 @@ namespace MSFSPopoutPanelManager.Provider
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
}
public static void LeftClickFast(int x, int y)
{
PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, x, y, 0, 0);
Thread.Sleep(50);
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, y, 0, 0);
}
public static void PopOutPanel(int x, int y)
{
LeftClick(x, y);

View file

@ -16,6 +16,9 @@ namespace MSFSPopoutPanelManager.Provider
public const int SW_MINIMIZE = 6;
public const int SW_RESTORE = 9;
public const uint EVENT_SYSTEM_CAPTURESTART = 0x0008;
public const uint EVENT_SYSTEM_CAPTUREEND = 0x0009;
public const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A;
public const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
public const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B;

View file

@ -1,7 +1,10 @@
using MSFSPopoutPanelManager.Model;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MSFSPopoutPanelManager.Provider
{
@ -11,6 +14,8 @@ namespace MSFSPopoutPanelManager.Provider
private IntPtr _winEventHook;
private static PInvoke.WinEventProc _winEvent; // keep this as static to prevent garbage collect or the app will crash
private Rectangle _lastWindowRectangle;
private uint _prevWinEvent = PInvokeConstant.EVENT_SYSTEM_CAPTUREEND;
private int _winEventClickLock = 0;
public UserProfile UserProfile { get; set; }
@ -26,7 +31,7 @@ namespace MSFSPopoutPanelManager.Provider
public void HookWinEvent()
{
// Setup panel config event hooks
_winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, DiagnosticManager.GetApplicationProcess().Handle, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT);
_winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_CAPTURESTART, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, DiagnosticManager.GetApplicationProcess().Handle, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT);
}
public void UnhookWinEvent()
@ -173,34 +178,46 @@ namespace MSFSPopoutPanelManager.Provider
private void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
{
PanelConfig panelConfig;
// check by priority to minimize escaping constraint
switch(iEvent)
{
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
case PInvokeConstant.EVENT_SYSTEM_CAPTURESTART:
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
// check by priority to speed up comparing of escaping constraints
if (hWnd == IntPtr.Zero
|| idObject != 0
|| hWinEventHook != _winEventHook
|| !AllowEdit
|| !(iEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE || iEvent == PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND)
|| UserProfile.PanelConfigs == null || UserProfile.PanelConfigs.Count == 0)
|| UserProfile.PanelConfigs == null
|| UserProfile.PanelConfigs.Count == 0)
{
return;
}
if(UserProfile.IsLocked)
{
panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd);
HandleEventCallback(hWnd, iEvent);
break;
default:
break;
}
}
if (panelConfig != null && panelConfig.PanelType == PanelType.CustomPopout)
private void HandleEventCallback(IntPtr hWnd, uint iEvent)
{
// Move window back to original location if user profile is locked
if (iEvent == PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND)
var panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd);
if (panelConfig == null)
return;
if (panelConfig.IsLockable && UserProfile.IsLocked)
{
switch (iEvent)
{
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
// Move window back to original location
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
return;
}
if (iEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE)
{
break;
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
// Detect if window is maximized, if so, save settings
WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp);
@ -209,16 +226,19 @@ namespace MSFSPopoutPanelManager.Provider
{
PInvoke.ShowWindow(hWnd, PInvokeConstant.SW_RESTORE);
}
return;
break;
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE)
break;
if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTUREEND)
break;
HandleTouchEvent(panelConfig);
break;
}
}
return;
}
panelConfig = UserProfile.PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd);
if (panelConfig != null)
else
{
switch (iEvent)
{
@ -260,7 +280,53 @@ namespace MSFSPopoutPanelManager.Provider
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
_userProfileManager.WriteUserProfiles();
break;
}
case PInvokeConstant.EVENT_SYSTEM_CAPTUREEND:
if (!panelConfig.TouchEnabled || _prevWinEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE)
break;
if (!panelConfig.HasTouchableEvent || _prevWinEvent == PInvokeConstant.EVENT_SYSTEM_CAPTUREEND)
break;
HandleTouchEvent(panelConfig);
break;
}
}
_prevWinEvent = iEvent;
}
private void HandleTouchEvent(PanelConfig panelConfig)
{
Point point;
PInvoke.GetCursorPos(out point);
// Disable left clicking if user is touching the title bar area
if (point.Y - panelConfig.Top > (panelConfig.HideTitlebar ? 5 : 31))
{
var prevWinEventClickLock = ++_winEventClickLock;
UnhookWinEvent();
InputEmulationManager.LeftClickFast(point.X, point.Y);
HookWinEvent();
if (prevWinEventClickLock == _winEventClickLock)
{
Task.Run(() => RefocusMsfs(prevWinEventClickLock));
}
}
}
private async Task RefocusMsfs(int prevWinEventClickLock)
{
Thread.Sleep(1000);
if (prevWinEventClickLock == _winEventClickLock)
{
var simulatorProcess = DiagnosticManager.GetSimulatorProcess();
Rectangle rectangle;
PInvoke.GetWindowRect(simulatorProcess.Handle, out rectangle);
PInvoke.SetCursorPos(rectangle.X + 18, rectangle.Y + 80);
}
}
}

View file

@ -215,13 +215,13 @@ namespace MSFSPopoutPanelManager.Provider
});
// Remove pop out that do not exist for this pop out iteration
foreach(var panelConfig in UserProfile.PanelConfigs.ToList())
{
if(panelConfig.PanelHandle == IntPtr.Zero)
{
UserProfile.PanelConfigs.Remove(panelConfig);
}
}
//foreach(var panelConfig in UserProfile.PanelConfigs.ToList())
//{
// if(panelConfig.PanelHandle == IntPtr.Zero)
// {
// UserProfile.PanelConfigs.Remove(panelConfig);
// }
//}
Parallel.ForEach(UserProfile.PanelConfigs, panel =>
{
@ -254,6 +254,14 @@ namespace MSFSPopoutPanelManager.Provider
PInvoke.MoveWindow(panel.PanelHandle, panel.Left, panel.Top, panel.Width, panel.Height, false);
Thread.Sleep(1000);
// Built-in panels (ie. Checklist, ATC) needs another window resize since there is a bug where when move between
// monitors, it changes its size
if(panel.PanelType == PanelType.BuiltInPopout)
{
PInvoke.MoveWindow(panel.PanelHandle, panel.Left, panel.Top, panel.Width, panel.Height, false);
Thread.Sleep(1000);
}
// Apply always on top
if (panel.AlwaysOnTop)
{

View file

@ -7,7 +7,7 @@
<RootNamespace>MSFSPopoutPanelManager.Provider</RootNamespace>
<PackageId>MSFS 2020 Popout Panel Manager Provider</PackageId>
<Product>MSFS 2020 Popout Panel Manager Provider</Product>
<Version>3.3.3.0</Version>
<Version>3.4.0.0</Version>
<Authors>Stanley Kwok</Authors>
<Copyright>Stanley Kwok 2021</Copyright>
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>

View file

@ -9,10 +9,10 @@
<Company>Stanley Kwok</Company>
<Copyright>Stanley Kwok 2021</Copyright>
<PackageProjectUrl>https://github.com/hawkeye-stan/msfs-popout-panel-manager</PackageProjectUrl>
<Version>3.3.3.0</Version>
<Version>3.4.0.0</Version>
<Platforms>AnyCPU;x64</Platforms>
<UseWPF>true</UseWPF>
<AssemblyVersion>3.3.3.0</AssemblyVersion>
<AssemblyVersion>3.4.0.0</AssemblyVersion>
</PropertyGroup>
<ItemGroup>

View file

@ -91,10 +91,10 @@
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Panel Name" Width="250" >
<DataGridTemplateColumn Header="Panel Name" Width="230" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="PanelName" Width="250" BorderThickness="0" TextAlignment="Left" Text="{Binding Path=PanelName, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
<TextBox Name="PanelName" Width="230" BorderThickness="0" TextAlignment="Left" Text="{Binding Path=PanelName, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{Binding Path=IsCustomPopout, Mode=OneWay}">
<TextBox.Style>
<Style TargetType="TextBox">
@ -117,10 +117,10 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="X-Pos" Width="90">
<DataGridTemplateColumn Header="X-Pos" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="Left" Width="90" BorderThickness="0"
<TextBox Name="Left" Width="80" BorderThickness="0"
Text="{Binding Path=Left, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated"
Style="{StaticResource TextBoxColumnFocus}"
@ -128,10 +128,10 @@
IsHitTestVisible="{c:Binding Path='!FullScreen'}"/> </DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Y-Pos" Width="90">
<DataGridTemplateColumn Header="Y-Pos" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="Top" Width="90" BorderThickness="0"
<TextBox Name="Top" Width="80" BorderThickness="0"
Text="{Binding Path=Top, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated"
Style="{StaticResource TextBoxColumnFocus}"
@ -140,10 +140,10 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Width" Width="90">
<DataGridTemplateColumn Header="Width" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="Width" Width="90" BorderThickness="0"
<TextBox Name="Width" Width="80" BorderThickness="0"
Text="{Binding Path=Width, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated"
Style="{StaticResource TextBoxColumnFocus}"
@ -152,10 +152,10 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Height" Width="90">
<DataGridTemplateColumn Header="Height" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Name="Height" Width="90" BorderThickness="0"
<TextBox Name="Height" Width="80" BorderThickness="0"
Text="{Binding Path=Height, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated"
Style="{StaticResource TextBoxColumnFocus}"
@ -164,10 +164,10 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Always on Top" Width="90">
<DataGridTemplateColumn Header="Always on Top" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="AlwaysOnTop" Width="90" Margin="40 0 0 0"
<CheckBox Name="AlwaysOnTop" Width="80" Margin="30 0 0 0"
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding Path=AlwaysOnTop, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.DataStore.ActiveUserProfile.IsLocked and DataContext.DataStore.AllowEdit'}"
@ -175,10 +175,10 @@
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Hide Title Bar" Width="90">
<DataGridTemplateColumn Header="Hide Title Bar" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="HideTitlebar" Width="90" Margin="40 0 0 0"
<CheckBox Name="HideTitlebar" Width="80" Margin="30 0 0 0"
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding Path=HideTitlebar, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.DataStore.ActiveUserProfile.IsLocked and DataContext.DataStore.AllowEdit'}"
@ -189,13 +189,23 @@
<DataGridTemplateColumn Header="Full Screen Mode" Width="90">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="FullScreen" Width="90" Margin="40 0 0 0"
<CheckBox Name="FullScreen" Width="90" Margin="35 0 0 0"
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding Path=FullScreen, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.DataStore.ActiveUserProfile.IsLocked and DataContext.DataStore.AllowEdit'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Touch Enabled" Width="80">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Name="TouchEnabled" Width="80" Margin="30 0 0 0"
SourceUpdated="GridData_SourceUpdated"
IsChecked="{Binding Path=TouchEnabled, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"
IsEnabled="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='!DataContext.DataStore.ActiveUserProfile.IsLocked and DataContext.DataStore.AllowEdit'}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</WrapPanel>

View file

@ -68,6 +68,9 @@ namespace MSFSPopoutPanelManager.WpfApp
case "Full Screen Mode":
selectedProperty = PanelConfigPropertyName.FullScreen;
break;
case "Touch Enabled":
selectedProperty = PanelConfigPropertyName.TouchEnabled;
break;
}
_panelConfigurationViewModel.SelectedPanelConfigItem = new PanelConfigItem() { PanelIndex = panelConfig.PanelIndex, PanelConfigProperty = selectedProperty };

View file

@ -4,7 +4,7 @@
<OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Version>3.3.3.0</Version>
<Version>3.4.0.0</Version>
<PackageId>MSFS 2020 Popout Panel Manager</PackageId>
<Authors>Stanley Kwok</Authors>
<Product>MSFS 2020 Popout Panel Manager</Product>