mirror of
https://github.com/hawkeye-stan/msfs-popout-panel-manager.git
synced 2024-11-22 05:40:11 +00:00
Preview release
This commit is contained in:
parent
e84fac2123
commit
96b5ae8d8d
8 changed files with 187 additions and 54 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ namespace MSFSPopoutPanelManager.Model
|
|||
AlwaysOnTop,
|
||||
HideTitlebar,
|
||||
FullScreen,
|
||||
TouchEnabled,
|
||||
Invalid
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,8 +280,54 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 };
|
||||
|
|
Loading…
Reference in a new issue