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

Version 3.2 Release

This commit is contained in:
hawkeye 2022-02-06 22:05:05 -05:00
parent 39d4d98be6
commit 973ebd3dd7
18 changed files with 164 additions and 72 deletions

View file

@ -16,6 +16,8 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{15FC98CD-0A69-437B-A5E5-67D025DB5CDC}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{15FC98CD-0A69-437B-A5E5-67D025DB5CDC}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.gitignore = .gitignore .gitignore = .gitignore
autoupdate.xml = autoupdate.xml
latestreleasenotes.txt = latestreleasenotes.txt
LICENSE = LICENSE LICENSE = LICENSE
README.md = README.md README.md = README.md
VERSION.md = VERSION.md VERSION.md = VERSION.md

View file

@ -141,7 +141,7 @@ namespace MSFSPopoutPanelManager.Model
{ {
public AutoPopOutPanelsWaitDelay() public AutoPopOutPanelsWaitDelay()
{ {
ReadyToFlyButton = 2; ReadyToFlyButton = 4;
InitialCockpitView = 2; InitialCockpitView = 2;
InstrumentationPowerOn = 2; InstrumentationPowerOn = 2;
} }

View file

@ -1,4 +1,5 @@
using System; using MSFSPopoutPanelManager.Shared;
using System;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Threading; using System.Threading;
@ -9,6 +10,9 @@ namespace MSFSPopoutPanelManager.Provider
{ {
public static Bitmap TakeScreenShot(IntPtr windowHandle) public static Bitmap TakeScreenShot(IntPtr windowHandle)
{ {
if (!PInvoke.IsWindow(windowHandle))
throw new PopoutManagerException("Pop out windows were closed unexpectedly.");
// Set window to foreground so nothing can hide the window // Set window to foreground so nothing can hide the window
PInvoke.SetForegroundWindow(windowHandle); PInvoke.SetForegroundWindow(windowHandle);
Thread.Sleep(300); Thread.Sleep(300);
@ -16,19 +20,30 @@ namespace MSFSPopoutPanelManager.Provider
Rectangle rectangle; Rectangle rectangle;
PInvoke.GetWindowRect(windowHandle, out rectangle); PInvoke.GetWindowRect(windowHandle, out rectangle);
var left = rectangle.Left; Rectangle clientRectangle;
var top = rectangle.Top; PInvoke.GetClientRect(windowHandle, out clientRectangle);
var width = rectangle.Right - rectangle.Left;
var height = rectangle.Bottom - rectangle.Top;
var bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb); // Take a screen shot by removing the titlebar of the window
var left = rectangle.Left;
var top = rectangle.Top + (rectangle.Height - clientRectangle.Height) - 8; // 8 pixels adjustments
var bmp = new Bitmap(clientRectangle.Width, clientRectangle.Height, PixelFormat.Format24bppRgb);
using (Graphics g = Graphics.FromImage(bmp)) using (Graphics g = Graphics.FromImage(bmp))
{ {
g.CopyFromScreen(new Point(left, top), Point.Empty, rectangle.Size); g.CopyFromScreen(new Point(left, top), Point.Empty, rectangle.Size);
} }
return bmp; // Place the above image in the same canvas size as before
Bitmap backingImage = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics gfx = Graphics.FromImage(backingImage))
using (SolidBrush brush = new SolidBrush(Color.FromArgb(255, 0, 0)))
{
gfx.FillRectangle(brush, 0, 0, rectangle.Width, rectangle.Height);
gfx.DrawImage(bmp, new Point(0, top));
}
return backingImage;
} }
} }
} }

View file

@ -2,11 +2,14 @@
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Threading; using System.Threading;
using WindowsInput.Events;
namespace MSFSPopoutPanelManager.Provider namespace MSFSPopoutPanelManager.Provider
{ {
public class InputEmulationManager public class InputEmulationManager
{ {
const uint MOUSEEVENTF_ABSOLUTE = 0x8000;
const uint MOUSEEVENTF_MOVE = 0x0001;
const uint MOUSEEVENTF_LEFTDOWN = 0x02; const uint MOUSEEVENTF_LEFTDOWN = 0x02;
const uint MOUSEEVENTF_LEFTUP = 0x04; const uint MOUSEEVENTF_LEFTUP = 0x04;
const uint KEYEVENTF_EXTENDEDKEY = 0x01; const uint KEYEVENTF_EXTENDEDKEY = 0x01;
@ -30,6 +33,9 @@ namespace MSFSPopoutPanelManager.Provider
public static void PopOutPanel(int x, int y) public static void PopOutPanel(int x, int y)
{ {
LeftClick(x, y);
Thread.Sleep(300);
PInvoke.SetCursorPos(x, y); PInvoke.SetCursorPos(x, y);
Thread.Sleep(300); Thread.Sleep(300);

View file

@ -38,7 +38,7 @@ namespace MSFSPopoutPanelManager.Provider
public static void EndHook() public static void EndHook()
{ {
if (_mouseHook == null) if (_mouseHook != null)
{ {
_mouseHook.MouseDownExt -= HandleMouseHookMouseDownExt; _mouseHook.MouseDownExt -= HandleMouseHookMouseDownExt;
_mouseHook.Dispose(); _mouseHook.Dispose();

View file

@ -39,10 +39,14 @@ namespace MSFSPopoutPanelManager.Provider
public class PInvoke public class PInvoke
{ {
[DllImport("user32")] [DllImport("user32")]
public static extern int EnumWindows(CallBack x, int y); public static extern int EnumWindows(CallBack callback, int lParam);
[DllImport("user32")] [DllImport("user32")]
private static extern bool EnumChildWindows(IntPtr window, CallBack callback, IntPtr lParam); public static extern bool EnumChildWindows(IntPtr window, CallBack callback, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", CharSet = CharSet.Auto)] [DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetClassName(IntPtr hWnd, StringBuilder strPtrClassName, Int32 nMaxCount); private static extern int GetClassName(IntPtr hWnd, StringBuilder strPtrClassName, Int32 nMaxCount);
@ -81,6 +85,10 @@ namespace MSFSPopoutPanelManager.Provider
return sb.ToString(); return sb.ToString();
} }
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32.dll")] [DllImport("user32.dll")]
public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);

View file

@ -61,10 +61,18 @@ namespace MSFSPopoutPanelManager.Provider
break; break;
case PanelConfigPropertyName.Left: case PanelConfigPropertyName.Left:
case PanelConfigPropertyName.Top: case PanelConfigPropertyName.Top:
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar)
return;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true); PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true);
break; break;
case PanelConfigPropertyName.Width: case PanelConfigPropertyName.Width:
case PanelConfigPropertyName.Height: case PanelConfigPropertyName.Height:
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar)
return;
int orignalLeft = panelConfig.Left; int orignalLeft = panelConfig.Left;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true); PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height); MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height);
@ -92,6 +100,10 @@ namespace MSFSPopoutPanelManager.Provider
{ {
var panelConfig = UserProfile.PanelConfigs[index]; var panelConfig = UserProfile.PanelConfigs[index];
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar)
return;
int orignalLeft = panelConfig.Left; int orignalLeft = panelConfig.Left;
switch (panelConfigItem.PanelConfigProperty) switch (panelConfigItem.PanelConfigProperty)

View file

@ -3,6 +3,7 @@ using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.Linq; using System.Linq;
@ -111,62 +112,40 @@ namespace MSFSPopoutPanelManager.Provider
_panels.Clear(); _panels.Clear();
if(_simulatorHandle != IntPtr.Zero) if(_simulatorHandle != IntPtr.Zero)
PInvoke.SetForegroundWindow(_simulatorHandle); PInvoke.SetForegroundWindow(_simulatorHandle);
try try
{ {
for (var i = 0; i < UserProfile.PanelSourceCoordinates.Count; i++) // PanelIndex starts at 1
for (var i = 1; i <= UserProfile.PanelSourceCoordinates.Count; i++)
{ {
PopoutPanel(UserProfile.PanelSourceCoordinates[i].X, UserProfile.PanelSourceCoordinates[i].Y); PopoutPanel(UserProfile.PanelSourceCoordinates[i - 1].X, UserProfile.PanelSourceCoordinates[i - 1].Y);
if (i == 0) if (i > 1)
{ {
int retry = 0; SeparatePanel(i - 1, _panels[0].PanelHandle); // The joined panel is always the first panel that got popped out
while (retry < RETRY_COUNT)
{
PInvoke.EnumWindows(new PInvoke.CallBack(EnumCustomPopoutCallBack), 0);
if (GetPopoutPanelCountByType(PanelType.CustomPopout) == 0)
retry += 1;
else
{
var panel = GetCustomPopoutPanelByIndex(i);
panel.PanelName = $"Panel{i + 1}";
PInvoke.SetWindowText(panel.PanelHandle, panel.PanelName + " (Custom)");
break;
}
}
if (GetPopoutPanelCountByType(PanelType.CustomPopout) != i + 1)
throw new PopoutManagerException("Unable to pop out the first panel. Please check the first panel's number circle is positioned inside the panel, check for panel obstruction, and check if panel can be popped out. Pop out process stopped.");
} }
if (i >= 1) // only separate with 2 or more panels
{
int retry = 0;
while (retry < RETRY_COUNT)
{
SeparatePanel(i, _panels[0].PanelHandle); // The joined panel is always the first panel that got popped out
PInvoke.EnumWindows(new PInvoke.CallBack(EnumCustomPopoutCallBack), i);
if (GetPopoutPanelCountByType(PanelType.CustomPopout) != i + 1) var handle = PInvoke.FindWindow("AceApp", String.Empty);
retry += 1;
else
{
// Panel has successfully popped out
var panel = GetCustomPopoutPanelByIndex(i);
PInvoke.MoveWindow(panel.PanelHandle, 0, 0, 800, 600, true); if(handle == IntPtr.Zero && i == 1)
panel.PanelName = $"Panel{i + 1}"; throw new PopoutManagerException("Unable to pop out the first panel. Please check the first panel's number circle is positioned inside the panel, check for panel obstruction, and check if panel can be popped out. Pop out process stopped.");
PInvoke.SetWindowText(panel.PanelHandle, panel.PanelName + " (Custom)"); else if(handle == IntPtr.Zero)
break; throw new PopoutManagerException($"Unable to pop out panel number {i}. Please check panel's number circle is positioned inside the panel, check for panel obstruction, and check if panel can be popped out. Pop out process stopped.");
}
}
if (GetPopoutPanelCountByType(PanelType.CustomPopout) != i + 1) var panelInfo = GetPanelWindowInfo(handle);
throw new PopoutManagerException($"Unable to pop out panel number {i + 1}. Please check panel's number circle is positioned inside the panel, check for panel obstruction, and check if panel can be popped out. Pop out process stopped."); panelInfo.PanelIndex = i;
} panelInfo.PanelName = $"Panel{i}";
_panels.Add(panelInfo);
PInvoke.SetWindowText(panelInfo.PanelHandle, panelInfo.PanelName + " (Custom)");
if (i > 1)
PInvoke.MoveWindow(panelInfo.PanelHandle, 0, 0, 800, 600, true);
} }
_currentPanelIndex = _panels.Count;
// Performance validation, make sure the number of pop out panels is equal to the number of selected panel // Performance validation, make sure the number of pop out panels is equal to the number of selected panel
if (GetPopoutPanelCountByType(PanelType.CustomPopout) != UserProfile.PanelSourceCoordinates.Count) if (GetPopoutPanelCountByType(PanelType.CustomPopout) != UserProfile.PanelSourceCoordinates.Count)
throw new PopoutManagerException("Unable to pop out all panels. Please align all panel number circles with in-game panel locations."); throw new PopoutManagerException("Unable to pop out all panels. Please align all panel number circles with in-game panel locations.");
@ -293,8 +272,6 @@ namespace MSFSPopoutPanelManager.Provider
private void PopoutPanel(int x, int y) private void PopoutPanel(int x, int y)
{ {
InputEmulationManager.LeftClick(x, y);
Thread.Sleep(200);
InputEmulationManager.PopOutPanel(x, y); InputEmulationManager.PopOutPanel(x, y);
} }
@ -309,13 +286,10 @@ namespace MSFSPopoutPanelManager.Provider
// Find the magnifying glass coordinate // Find the magnifying glass coordinate
var point = AnalyzeMergedWindows(hwnd); var point = AnalyzeMergedWindows(hwnd);
if (point.Y <= 39) // false positive
return;
InputEmulationManager.LeftClick(point.X, point.Y); InputEmulationManager.LeftClick(point.X, point.Y);
} }
public bool EnumCustomPopoutCallBack(IntPtr hwnd, int index) public bool EnumCustomPopoutCallBack(IntPtr hwnd, int lParam)
{ {
var panelInfo = GetPanelWindowInfo(hwnd); var panelInfo = GetPanelWindowInfo(hwnd);
@ -324,7 +298,7 @@ namespace MSFSPopoutPanelManager.Provider
if (!_panels.Exists(x => x.PanelHandle == hwnd)) if (!_panels.Exists(x => x.PanelHandle == hwnd))
{ {
Interlocked.Increment(ref _currentPanelIndex); Interlocked.Increment(ref _currentPanelIndex);
panelInfo.PanelIndex = _currentPanelIndex; // PanelIndex starts at 1 panelInfo.PanelIndex = _currentPanelIndex;
_panels.Add(panelInfo); _panels.Add(panelInfo);
} }
} }
@ -332,7 +306,7 @@ namespace MSFSPopoutPanelManager.Provider
return true; return true;
} }
public bool EnumBuiltinPopoutCallBack(IntPtr hwnd, int index) public bool EnumBuiltinPopoutCallBack(IntPtr hwnd, int lParam)
{ {
var panelInfo = GetPanelWindowInfo(hwnd); var panelInfo = GetPanelWindowInfo(hwnd);
@ -360,6 +334,9 @@ namespace MSFSPopoutPanelManager.Provider
Interlocked.Increment(ref _currentPanelIndex); Interlocked.Increment(ref _currentPanelIndex);
panelInfo.PanelIndex = _currentPanelIndex; panelInfo.PanelIndex = _currentPanelIndex;
_panels.Add(panelInfo); _panels.Add(panelInfo);
// Apply always on top to these panels
WindowManager.ApplyAlwaysOnTop(panelInfo.PanelHandle, true);
} }
} }
@ -374,9 +351,16 @@ namespace MSFSPopoutPanelManager.Provider
{ {
var caption = PInvoke.GetWindowText(hwnd); var caption = PInvoke.GetWindowText(hwnd);
Rectangle rectangle;
PInvoke.GetWindowRect(hwnd, out rectangle);
var panelInfo = new PanelConfig(); var panelInfo = new PanelConfig();
panelInfo.PanelHandle = hwnd; panelInfo.PanelHandle = hwnd;
panelInfo.PanelName = caption; panelInfo.PanelName = caption;
panelInfo.Top = rectangle.Top;
panelInfo.Left = rectangle.Left;
panelInfo.Width = rectangle.Width;
panelInfo.Height = rectangle.Height;
if (String.IsNullOrEmpty(caption) || caption.IndexOf("Custom") > -1) if (String.IsNullOrEmpty(caption) || caption.IndexOf("Custom") > -1)
panelInfo.PanelType = PanelType.CustomPopout; panelInfo.PanelType = PanelType.CustomPopout;
@ -411,6 +395,9 @@ namespace MSFSPopoutPanelManager.Provider
{ {
var sourceImage = ImageOperation.TakeScreenShot(hwnd); var sourceImage = ImageOperation.TakeScreenShot(hwnd);
if (sourceImage == null)
return new Point(0, 0);
Rectangle rectangle; Rectangle rectangle;
PInvoke.GetClientRect(hwnd, out rectangle); PInvoke.GetClientRect(hwnd, out rectangle);

View file

@ -36,6 +36,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="MouseKeyHook" Version="5.6.0" /> <PackageReference Include="MouseKeyHook" Version="5.6.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="WindowsInput" Version="6.4.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -137,6 +137,8 @@ The user plane profile data and application settings data are stored as JSON fil
## Current Known Issue ## Current Known Issue
* Automatic power on for Auto Pop Out Panel feature will not work if you're using any flight control hardware (such as Honeycomb Alpha or Bravo) that permanently binds the master battery switch or master avionics switch. If the hardware control switch is in the off position, pop out manager won't be able to temporary turn on the instrumentation panels to pop them out. This seems to be a bug on Asobo side and only affects the G1000 instrumentation at the moment.
* Sometimes when using the auto-panning feature, the keyboard combination of Ctrl-Alt-0 and Alt-0 do not work to save and load panel panning coordinates. First try to restart the flightsim and it usually fixes the problem. Otherwise, the only way to fix this is to redo the profile if you want the auto-panning feature since the camera angle is only being saved during the initial creation of the profile. The is another MSFS bug. * Sometimes when using the auto-panning feature, the keyboard combination of Ctrl-Alt-0 and Alt-0 do not work to save and load panel panning coordinates. First try to restart the flightsim and it usually fixes the problem. Otherwise, the only way to fix this is to redo the profile if you want the auto-panning feature since the camera angle is only being saved during the initial creation of the profile. The is another MSFS bug.
* Current application package size is bigger than previous version of the application because it is not a single EXE file package. With added feature of exception logging and stack trace to support user feedback and troubleshooting, a Single EXE package in .NET 5.0 as well as .NET 6.0 has a bug that stack trace information is not complete. Hopefully, Microsoft will be fixing this problem. * Current application package size is bigger than previous version of the application because it is not a single EXE file package. With added feature of exception logging and stack trace to support user feedback and troubleshooting, a Single EXE package in .NET 5.0 as well as .NET 6.0 has a bug that stack trace information is not complete. Hopefully, Microsoft will be fixing this problem.
@ -180,3 +182,5 @@ Thank you for your super kind support of this app!
[WPF CalcBinding](https://github.com/Alex141/CalcBinding) by Alexander Zinchenko [WPF CalcBinding](https://github.com/Alex141/CalcBinding) by Alexander Zinchenko
[AutoUpdater.NET](https://github.com/ravibpatel/AutoUpdater.NET) by Ravi Patel

View file

@ -2,7 +2,13 @@
<hr/> <hr/>
## Version 3.2.0 (Release)
* Added application auto update support. By installing this version of the app, auto update functionality will be available for all future versions of the application.
* Disabled panel adjustments when Hide Title Bar is checked for a panel. This is to fix an issue where panel adjustments (X-Pos, Y-Pos, Width, and Height) will behave erratically when Hide Title Bar is checked.
* Increased default delay for auto-clicking "Ready to Fly" button from 2 seconds to 4 seconds in regard to Auto Pop Out Panels feature . [Fixed Issue #9](https://github.com/hawkeye-stan/msfs-popout-panel-manager/issues/9)
## Version 3.2.0 (Beta) ## Version 3.2.0 (Beta)
* Added new Auto Pop Out Panels when flight start feature. Now the app will match a profile to the plane you're flying and perform all the pop outs for you, even help you to click the "ready to fly" button when a flying session is about to start!
* Added per monitor DPI-awareness support. The application should run and display correctly when using combination of mixed monitor (with high-DPI and low-DPI) resolutions and scaling. * Added per monitor DPI-awareness support. The application should run and display correctly when using combination of mixed monitor (with high-DPI and low-DPI) resolutions and scaling.
* Added system tray icon access. Application can start minimize or minimize to system tray. System tray icon features a context menu to allow quick access to application functions. * Added system tray icon access. Application can start minimize or minimize to system tray. System tray icon features a context menu to allow quick access to application functions.
* Added user requested feature to provide keyboard shortcut (Ctrl-Alt-P) to start panel pop out with either an active profile or a default profile selected. * Added user requested feature to provide keyboard shortcut (Ctrl-Alt-P) to start panel pop out with either an active profile or a default profile selected.

View file

@ -76,7 +76,6 @@ namespace MSFSPopoutPanelManager.WpfApp
var messageBoxMessage = "Application has encountered a critical error and will be closed.\nPlease see the file 'error.log' for information."; var messageBoxMessage = "Application has encountered a critical error and will be closed.\nPlease see the file 'error.log' for information.";
var messageBoxButtons = MessageBoxButton.OK; var messageBoxButtons = MessageBoxButton.OK;
// Let the user decide if the app should die or not (if applicable).
if (MessageBox.Show(messageBoxMessage, messageBoxTitle, messageBoxButtons) == MessageBoxResult.OK) if (MessageBox.Show(messageBoxMessage, messageBoxTitle, messageBoxButtons) == MessageBoxResult.OK)
{ {
Application.Current.Shutdown(); Application.Current.Shutdown();

View file

@ -17,6 +17,17 @@
<Trigger Property="IsFocused" Value="true"> <Trigger Property="IsFocused" Value="true">
<Setter Property="Background" Value="#FF576573" /> <Setter Property="Background" Value="#FF576573" />
</Trigger> </Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border Background="Transparent" BorderBrush="Transparent" BorderThickness="0">
<TextBlock Text="{TemplateBinding Text}" Padding="4" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
</ResourceDictionary> </ResourceDictionary>
@ -110,7 +121,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Left" Width="100" BorderThickness="0" Text="{Binding Path=Left, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Left" Width="100" BorderThickness="0" Text="{Binding Path=Left, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -118,7 +129,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Top" Width="100" BorderThickness="0" Text="{Binding Path=Top, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Top" Width="100" BorderThickness="0" Text="{Binding Path=Top, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -126,7 +137,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Width" Width="100" BorderThickness="0" Text="{Binding Path=Width, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Width" Width="100" BorderThickness="0" Text="{Binding Path=Width, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -134,7 +145,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Height" Width="100" BorderThickness="0" Text="{Binding Path=Height, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Height" Width="100" BorderThickness="0" Text="{Binding Path=Height, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>

View file

@ -1,10 +1,11 @@
using MSFSPopoutPanelManager.FsConnector; using AutoUpdaterDotNET;
using MSFSPopoutPanelManager.Model; using MSFSPopoutPanelManager.Model;
using MSFSPopoutPanelManager.Provider; using MSFSPopoutPanelManager.Provider;
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
@ -92,7 +93,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
PanelConfigurationViewModel = new PanelConfigurationViewModel(DataStore, _userProfileManager); PanelConfigurationViewModel = new PanelConfigurationViewModel(DataStore, _userProfileManager);
InputHookManager.OnStartPopout += (source, e) => { OnStartPopOut(null); }; InputHookManager.OnStartPopout += (source, e) => { OnStartPopOut(null); };
InputHookManager.StartHook();
} }
public void Initialize() public void Initialize()
@ -110,6 +110,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
DeativateAutoPanelPopOut(); DeativateAutoPanelPopOut();
}; };
CheckForAutoUpdate();
// Set application version // Set application version
ApplicationVersion = DiagnosticManager.GetApplicationVersion(); ApplicationVersion = DiagnosticManager.GetApplicationVersion();
@ -128,6 +130,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
ShowPanelSelection(true); ShowPanelSelection(true);
IsMinimizedAllPanels = false; IsMinimizedAllPanels = false;
InputHookManager.StartHook();
} }
private void OnRestart(object commandParameter) private void OnRestart(object commandParameter)
@ -274,5 +278,17 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
DataStore.IsEnteredFlight = false; DataStore.IsEnteredFlight = false;
} }
private void CheckForAutoUpdate()
{
string jsonPath = Path.Combine(Path.Combine(FileIo.GetUserDataFilePath(), "autoupdate.json"));
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath);
AutoUpdater.Synchronous = true;
AutoUpdater.AppTitle = "MSFS Pop Out Panel Manager";
AutoUpdater.RunUpdateAsAdmin = false;
AutoUpdater.UpdateFormSize = new System.Drawing.Size(930, 675);
//AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml");
AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/AutoUpdateTest/main/autoupdate.xml");
}
} }
} }

View file

@ -125,7 +125,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
return; return;
} }
if (DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0) if (DataStore.ActiveUserProfile != null && DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0)
{ {
Logger.LogStatus("Panels pop out in progress.....", StatusMessageType.Info); Logger.LogStatus("Panels pop out in progress.....", StatusMessageType.Info);

View file

@ -4,7 +4,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework> <TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<Version>3.2.0</Version> <Version>3.2.0.0</Version>
<PackageId>MSFS 2020 Popout Panel Manager</PackageId> <PackageId>MSFS 2020 Popout Panel Manager</PackageId>
<Authors>Stanley Kwok</Authors> <Authors>Stanley Kwok</Authors>
<Product>MSFS 2020 Popout Panel Manager</Product> <Product>MSFS 2020 Popout Panel Manager</Product>
@ -50,6 +50,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.0" />
<PackageReference Include="CalcBinding" Version="2.5.2" /> <PackageReference Include="CalcBinding" Version="2.5.2" />
<PackageReference Include="Fody" Version="6.6.0"> <PackageReference Include="Fody" Version="6.6.0">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

7
autoupdate.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>3.2.0.0</version>
<url>https://github.com/hawkeye-stan/msfs-popout-panel-manager/releases/download/v3.2b/msfs-popout-panel-manager.zip</url>
<changelog>https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/latestreleasenotes.txt</changelog>
<mandatory>false</mandatory>
</item>

17
latestreleasenotes.txt Normal file
View file

@ -0,0 +1,17 @@
- Added per monitor DPI-awareness support. The application should run and display correctly when using combination of mixed monitor (with high-DPI and low-DPI) resolutions and scaling.
- Added system tray icon access. Application can start minimize or minimize to system tray. System tray icon features a context menu to allow quick access to application functions.
- Added user requested feature to provide keyboard shortcut (Ctrl-Alt-P) to start panel pop out with either an active profile or a default profile selected.
- New copy profile feature. You can reuse your defined panel settings for another plane or plane/livery combination.
- Added quick panel location selection adjustment feature. You can now adjust panel locations without redoing the entire profile.
- Added Save Auto Panning Camera Angle function if you need to adjust the in-game camera angle during panel selection.
- Added application auto update feature.
- New logo icon for the app.
- New dark theme for the entire UI.