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

Version 3.3.5 Release

This commit is contained in:
Stanley 2022-07-06 14:09:10 -04:00
parent a1fc7372d6
commit 03da2a1eee
23 changed files with 245 additions and 221 deletions

View file

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

View file

@ -27,7 +27,7 @@ namespace MSFSPopoutPanelManager.Model
StartMinimized = false; StartMinimized = false;
IncludeBuiltInPanel = false; IncludeBuiltInPanel = false;
AutoDisableTrackIR = true; AutoDisableTrackIR = true;
AutoPopOutPanels = false; AutoPopOutPanels = true;
AutoPopOutPanelsWaitDelay = new AutoPopOutPanelsWaitDelay(); AutoPopOutPanelsWaitDelay = new AutoPopOutPanelsWaitDelay();
} }
@ -150,9 +150,9 @@ namespace MSFSPopoutPanelManager.Model
public AutoPopOutPanelsWaitDelay() public AutoPopOutPanelsWaitDelay()
{ {
ReadyToFlyButton = 6; ReadyToFlyButton = 4;
InitialCockpitView = 2; InitialCockpitView = 1;
InstrumentationPowerOn = 2; InstrumentationPowerOn = 1;
} }
public int ReadyToFlyButton { get; set; } public int ReadyToFlyButton { get; set; }

View file

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

View file

@ -24,7 +24,7 @@ namespace MSFSPopoutPanelManager.Model
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
public ObservableCollection<PanelSourceCoordinate> PanelSourceCoordinates; public ObservableCollection<PanelSourceCoordinate> PanelSourceCoordinates { get; set; }
public ObservableCollection<PanelConfig> PanelConfigs { get; set; } public ObservableCollection<PanelConfig> PanelConfigs { get; set; }
@ -46,6 +46,12 @@ namespace MSFSPopoutPanelManager.Model
get { return BindingAircraftLiveries.Count > 0; } get { return BindingAircraftLiveries.Count > 0; }
} }
[JsonIgnore]
public bool HasPanelSourceCoordinates
{
get { return PanelSourceCoordinates.Count > 0; }
}
#region Legacy Properties #region Legacy Properties
// Support pre-Version 3.3 tag for one time conversion // Support pre-Version 3.3 tag for one time conversion

View file

@ -1,6 +1,8 @@
using System; using System;
using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace MSFSPopoutPanelManager.Provider namespace MSFSPopoutPanelManager.Provider
{ {
@ -37,6 +39,7 @@ namespace MSFSPopoutPanelManager.Provider
public static void PopOutPanel(int x, int y) public static void PopOutPanel(int x, int y)
{ {
//LeftClickSimulatorUpperLeftCorner();
LeftClick(x, y); LeftClick(x, y);
Thread.Sleep(300); Thread.Sleep(300);
@ -93,30 +96,39 @@ namespace MSFSPopoutPanelManager.Provider
PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYUP, 0);
} }
public static void LoadCustomView(IntPtr hwnd, string keybinding) public static void LoadCustomView(string keybinding)
{ {
uint customViewKey = (uint)(Convert.ToInt32(keybinding) + KEY_0); var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null)
{
// First center view to make sure recalling custom camera works on the first press
InputEmulationManager.CenterView(simualatorProcess.Handle);
PInvoke.SetForegroundWindow(hwnd); uint customViewKey = (uint)(Convert.ToInt32(keybinding) + KEY_0);
Thread.Sleep(500);
PInvoke.SetFocus(hwnd); PInvoke.SetForegroundWindow(simualatorProcess.Handle);
Thread.Sleep(300); Thread.Sleep(300);
// First center view using Ctrl-Space PInvoke.SetFocus(simualatorProcess.Handle);
PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYDOWN, 0); Thread.Sleep(300);
PInvoke.keybd_event(Convert.ToByte(VK_SPACE), 0, KEYEVENTF_KEYDOWN, 0);
Thread.Sleep(200);
PInvoke.keybd_event(Convert.ToByte(VK_SPACE), 0, KEYEVENTF_KEYUP, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYUP, 0);
Thread.Sleep(200);
// Then load view using Alt-0 // First center view using Ctrl-Space
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYDOWN, 0);
PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(VK_SPACE), 0, KEYEVENTF_KEYDOWN, 0);
Thread.Sleep(200); Thread.Sleep(200);
PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(VK_SPACE), 0, KEYEVENTF_KEYUP, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYUP, 0);
Thread.Sleep(200);
// Then load view using Alt-0
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0);
PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYDOWN, 0);
Thread.Sleep(200);
PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYUP, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYUP, 0);
Thread.Sleep(500);
}
} }
public static void ToggleFullScreenPanel(IntPtr hwnd) public static void ToggleFullScreenPanel(IntPtr hwnd)
@ -150,23 +162,26 @@ namespace MSFSPopoutPanelManager.Provider
Rectangle clientRectangle; Rectangle clientRectangle;
PInvoke.GetClientRect(hwnd, out clientRectangle); PInvoke.GetClientRect(hwnd, out clientRectangle);
// The "Ready to Fly" button is at about 94.7% width, 91.3% height at the lower right corner of game window // The "Ready to Fly" button is at about 94.7% width, 84% to 96.25% height (91.3% default) with interface scaling of 70 at the lower right corner of game window
// Try to click the area a few times to hit that button for both full screen and windows mode // Try to click the area a few times to hit that button for both full screen and windows mode
// set focus to game app
var x = Convert.ToInt32(rectangle.X + (clientRectangle.Width) * 0.947); var x = Convert.ToInt32(rectangle.X + (clientRectangle.Width) * 0.947);
var y = Convert.ToInt32(rectangle.Y + (clientRectangle.Height) * 0.9); var y = Convert.ToInt32(rectangle.Y + (clientRectangle.Height) * 0.84);
LeftClick(x, y); LeftClick(x, y);
Thread.Sleep(250);
LeftClick(x, y); LeftClick(x, y);
Thread.Sleep(250);
for (var top = y; top < y + 100; top = top + 20) for (var top = y; top < y + (clientRectangle.Height) * 0.125; top = top + 10)
{ {
LeftClick(x, Convert.ToInt32(top)); Debug.WriteLine($"Trying to click at x: {x} y: {Convert.ToInt32(top)}");
Thread.Sleep(100); PInvoke.SetCursorPos(x, Convert.ToInt32(top));
LeftClick(x, Convert.ToInt32(top));
Thread.Sleep(100); Thread.Sleep(100);
PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, x, Convert.ToInt32(top), 0, 0);
Thread.Sleep(200);
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, Convert.ToInt32(top), 0, 0);
Thread.Sleep(200);
PInvoke.mouse_event(MOUSEEVENTF_LEFTDOWN, x, Convert.ToInt32(top), 0, 0);
Thread.Sleep(200);
PInvoke.mouse_event(MOUSEEVENTF_LEFTUP, x, Convert.ToInt32(top), 0, 0);
} }
} }
} }

View file

@ -1,6 +1,5 @@
using Gma.System.MouseKeyHook; using Gma.System.MouseKeyHook;
using System; using System;
using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Windows.Forms; using System.Windows.Forms;
@ -19,19 +18,12 @@ namespace MSFSPopoutPanelManager.Provider
public static event EventHandler OnPanelSelectionCompleted; public static event EventHandler OnPanelSelectionCompleted;
public static event EventHandler<Point> OnPanelSelectionAdded; public static event EventHandler<Point> OnPanelSelectionAdded;
public static event EventHandler OnPanelSelectionRemoved; public static event EventHandler OnPanelSelectionRemoved;
public static event EventHandler OnStartPopout;
public static void StartHook() public static void StartHook()
{ {
if (_mouseHook == null) if (_mouseHook == null)
{ {
_mouseHook = Hook.GlobalEvents(); _mouseHook = Hook.GlobalEvents();
_mouseHook.OnCombination(new Dictionary<Combination, Action>
{
{Combination.FromString("Control+Alt+P"), () => { if(SubscribeToStartPopOutEvent) OnStartPopout?.Invoke(null, null); }}
});
_mouseHook.MouseDownExt += HandleMouseHookMouseDownExt; _mouseHook.MouseDownExt += HandleMouseHookMouseDownExt;
} }
} }
@ -42,6 +34,7 @@ namespace MSFSPopoutPanelManager.Provider
{ {
_mouseHook.MouseDownExt -= HandleMouseHookMouseDownExt; _mouseHook.MouseDownExt -= HandleMouseHookMouseDownExt;
_mouseHook.Dispose(); _mouseHook.Dispose();
_mouseHook = null;
} }
} }

View file

@ -45,19 +45,8 @@ namespace MSFSPopoutPanelManager.Provider
OnPopOutStarted?.Invoke(this, null); OnPopOutStarted?.Invoke(this, null);
// If enable, load the current viewport into custom view by Ctrl-Alt-0
if (AppSetting.UseAutoPanning) if (AppSetting.UseAutoPanning)
{ InputEmulationManager.LoadCustomView(AppSetting.AutoPanningKeyBinding);
var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null)
{
// First center view to make sure recalling custom camera works on the first press
InputEmulationManager.CenterView(simualatorProcess.Handle);
InputEmulationManager.LoadCustomView(simualatorProcess.Handle, AppSetting.AutoPanningKeyBinding);
Thread.Sleep(500);
}
}
Task<List<PanelConfig>> popoutPanelTask = Task<List<PanelConfig>>.Factory.StartNew(() => Task<List<PanelConfig>> popoutPanelTask = Task<List<PanelConfig>>.Factory.StartNew(() =>
{ {
@ -238,15 +227,7 @@ namespace MSFSPopoutPanelManager.Provider
Thread.Sleep(500); Thread.Sleep(500);
} }
// Apply full screen (cannot combine with always on top or hide title bar) if (!panel.FullScreen)
if (panel.FullScreen)
{
WindowManager.MoveWindow(panel.PanelHandle, panel.Left, panel.Top);
Thread.Sleep(1000);
InputEmulationManager.ToggleFullScreenPanel(panel.PanelHandle);
Thread.Sleep(1000);
}
else
{ {
// Apply locations // Apply locations
PInvoke.ShowWindow(panel.PanelHandle, PInvokeConstant.SW_RESTORE); PInvoke.ShowWindow(panel.PanelHandle, PInvokeConstant.SW_RESTORE);
@ -279,6 +260,19 @@ namespace MSFSPopoutPanelManager.Provider
PInvoke.ShowWindow(panel.PanelHandle, PInvokeConstant.SW_RESTORE); PInvoke.ShowWindow(panel.PanelHandle, PInvokeConstant.SW_RESTORE);
} }
}); });
// Apply full screen (cannot combine with always on top or hide title bar)
// Cannot run in parallel process
UserProfile.PanelConfigs.ToList().ForEach(panel =>
{
if (panel.FullScreen && (!panel.AlwaysOnTop && !panel.HideTitlebar))
{
WindowManager.MoveWindow(panel.PanelHandle, panel.Left, panel.Top);
Thread.Sleep(500);
InputEmulationManager.ToggleFullScreenPanel(panel.PanelHandle);
Thread.Sleep(250);
}
});
} }
private int GetPopoutPanelCountByType(PanelType panelType) private int GetPopoutPanelCountByType(PanelType panelType)

View file

@ -2,8 +2,6 @@
using MSFSPopoutPanelManager.Shared; using MSFSPopoutPanelManager.Shared;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace MSFSPopoutPanelManager.Provider namespace MSFSPopoutPanelManager.Provider
{ {
@ -12,10 +10,6 @@ namespace MSFSPopoutPanelManager.Provider
private UserProfileManager _userProfileManager; private UserProfileManager _userProfileManager;
private int _panelIndex; private int _panelIndex;
private List<PanelSourceCoordinate> _panelCoordinates; private List<PanelSourceCoordinate> _panelCoordinates;
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 bool _isEditingPanelCoordinates;
public event EventHandler OnPanelSelectionCompleted; public event EventHandler OnPanelSelectionCompleted;
public event EventHandler<EventArgs<PanelSourceCoordinate>> OnPanelLocationAdded; public event EventHandler<EventArgs<PanelSourceCoordinate>> OnPanelLocationAdded;
@ -39,8 +33,9 @@ namespace MSFSPopoutPanelManager.Provider
{ {
_panelIndex = 1; _panelIndex = 1;
_panelCoordinates = new List<PanelSourceCoordinate>(); _panelCoordinates = new List<PanelSourceCoordinate>();
ShowPanelLocationOverlay(_panelCoordinates, true); //ShowPanelLocationOverlay(_panelCoordinates, true);
InputHookManager.SubscribeToPanelSelectionEvent = true; InputHookManager.SubscribeToPanelSelectionEvent = true;
InputHookManager.StartHook();
} }
public void ShowPanelLocationOverlay(List<PanelSourceCoordinate> panelCoordinates, bool show) public void ShowPanelLocationOverlay(List<PanelSourceCoordinate> panelCoordinates, bool show)
@ -101,59 +96,5 @@ namespace MSFSPopoutPanelManager.Provider
InputHookManager.SubscribeToPanelSelectionEvent = false; InputHookManager.SubscribeToPanelSelectionEvent = false;
OnPanelSelectionCompleted?.Invoke(this, null); OnPanelSelectionCompleted?.Invoke(this, null);
} }
public void StartEditPanelLocations()
{
_winEvent = new PInvoke.WinEventProc(PanelLocationEditEventCallback);
_winEventHook = PInvoke.SetWinEventHook(PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND, PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE, DiagnosticManager.GetApplicationProcess().Handle, _winEvent, 0, 0, PInvokeConstant.WINEVENT_OUTOFCONTEXT);
_isEditingPanelCoordinates = true;
}
public void EndEditPanelLocations()
{
PInvoke.UnhookWinEvent(_winEventHook);
_isEditingPanelCoordinates = false;
}
private void PanelLocationEditEventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
{
// check by priority to minimize escaping constraint
if (hWnd == IntPtr.Zero
|| idObject != 0
|| hWinEventHook != _winEventHook
|| !_isEditingPanelCoordinates
|| !(iEvent == PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE || iEvent == PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND)
|| UserProfile.PanelSourceCoordinates == null || UserProfile.PanelSourceCoordinates.Count == 0)
{
return;
}
var panelSourceCoordinate = UserProfile.PanelSourceCoordinates.FirstOrDefault(panel => panel.PanelHandle == hWnd);
if (panelSourceCoordinate != null)
{
switch (iEvent)
{
case PInvokeConstant.EVENT_OBJECT_LOCATIONCHANGE:
Rectangle winRectangle;
PInvoke.GetWindowRect(panelSourceCoordinate.PanelHandle, out winRectangle);
if (_lastWindowRectangle == winRectangle) // ignore duplicate callback messages
return;
_lastWindowRectangle = winRectangle;
Rectangle clientRectangle;
PInvoke.GetClientRect(panelSourceCoordinate.PanelHandle, out clientRectangle);
panelSourceCoordinate.X = winRectangle.Left;
panelSourceCoordinate.Y = winRectangle.Top;
break;
case PInvokeConstant.EVENT_SYSTEM_MOVESIZEEND:
_userProfileManager.WriteUserProfiles();
break;
}
}
}
} }
} }

View file

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

View file

@ -87,6 +87,7 @@ namespace MSFSPopoutPanelManager.Provider
public void TurnOffTrackIR() public void TurnOffTrackIR()
{ {
Debug.Write("TrackIR OFF............" + Environment.NewLine);
if (_simData != null && _simData.TrackIREnable) if (_simData != null && _simData.TrackIREnable)
{ {
SetTrackIREnable(false); SetTrackIREnable(false);
@ -96,6 +97,7 @@ namespace MSFSPopoutPanelManager.Provider
public void TurnOnTrackIR() public void TurnOnTrackIR()
{ {
Debug.Write("TrackIR ON............" + Environment.NewLine);
if (_isTrackIRManaged && _simData != null && !_simData.TrackIREnable) if (_isTrackIRManaged && _simData != null && !_simData.TrackIREnable)
{ {
SetTrackIREnable(true); SetTrackIREnable(true);

View file

@ -38,37 +38,52 @@ What if you can do the setup once by defining on screen where the pop out panels
<hr> <hr>
## How to Use ## How to Install
[Online video - How to use](https://vimeo.com/723158934) 1. After downloading the latest zip package from github repository or from Flightsim.to website, extract the zip package to a folder of your choice on your computer. You must have write access to this folder since your preference settings and profiles will be save in the folder **userdata** within the installation folder. Please do not install in **C:\Program Files** or **C:\Program Files (x86)** since most users do not have write access to these folders.
Start the application **MSFSPopoutPanelManager.exe** and it will automatically connect when MSFS/SimConnect starts. You maybe prompt to download .NET framework 5.0 x64. Please see the screenshot below to download and install x64 desktop version of the framework. 2. Start the application **MSFSPopoutPanelManager.exe** and it will automatically connect when MSFS/SimConnect starts. You maybe prompt to download .NET framework 5.0 x64 desktop runtime. Please see the screenshot below to download and install x64 desktop version of the framework.
<p align="center"> <p align="center">
<img src="images/doc/framework_download.png" width="1000" hspace="10"/> <img src="images/doc/framework_download.png" width="1000" hspace="10"/>
</p> </p>
1. First start the game and start a flight. Then create a new profile (for example: Kodiak 100) by clicking the "plus" button in step 1 of the app. ## How to Update
1. To update the application, you can download the latest zip package and directly extract the package into your Pop Out Manager installation folder and overwrite all files within. Your **userdata** folder will be safe.
2. You can also use the built-in auto update feature and let the application handles the update. If the update is optional, you can skip the update if you so choose. When you start the application and if an update is available, a dialog will appear and it will show the latest version's release notes and an option to update the application.
3. If you're not being prompt for update when a new update is available, please try the following fixes:
- Restart you computer and most of the time this will do the trick.
- Clear your default web browser cache on your computer since auto update will try to download latest version of update configuration file from github repository and the file may have been cached on your machine.
## How to Use
[Online video - How to use](https://vimeo.com/723158934)
1. First start the application and the game. Once you're in the main menu of the game, you can create a new profile by clicking the "plus" button in step 1 of the app.
<p align="center"> <p align="center">
<img src="images/doc/add_profile.png" width="900" hspace="10"/> <img src="images/doc/add_profile.png" width="900" hspace="10"/>
</p> </p>
2. For step 2, if you want to associate the profile to the current aircraft livery to use in [Auto Pop Out](#auto-pop-out-feature) feature or for automatic profile switching when selecting a different aircraft, click the "plus" button next to the aircraft livery name. The aircraft livery title will become green once the livery is bound to the profile. 2. For step 2, if you want to associate the profile to the current aircraft livery to use in [Auto Pop Out](#auto-pop-out-feature) feature or for automatic profile switching when selecting a different aircraft, click the "plus" button next to the aircraft livery name. The aircraft livery title will become green once the livery is bound to the profile. Your chosen livery may not be available to select in the application for your newly selected plane until a flight is started.
<p align="center"> <p align="center">
<img src="images/doc/bind_profile_to_livery.png" width="900" hspace="10"/> <img src="images/doc/bind_profile_to_livery.png" width="900" hspace="10"/>
</p> </p>
3. Now start a flight with your chosen aircraft. Once your flight is started, you're ready to select the panels you want to pop out. Please click "Start Panel Selection" to define where the pop out panels will be using **LEFT CLICK**. Use **CTRL-LEFT CLICK** when selection is completed. You can also move the number circles at this point to do final adjustment. 3. Now start a flight with your chosen aircraft. Once your flight is started, you're ready to select the panels you want to pop out. Please click "Start Panel Selection" to define where the pop out panels will be using **LEFT CLICK**. Use **CTRL-LEFT CLICK** when selection is completed. You can also move the number circles at this point to do final adjustment.
<p align="center"> <p align="center">
<img src="images/doc/after_panel_selection.png" width="900" hspace="10"/> <img src="images/doc/after_panel_selection.png" width="900" hspace="10"/>
</p> </p>
4. Next, click "Start Pop Out". At this point, please be patient. The application will start popping out and separating panels one by one and you will see a lot of movements on screen. If something goes wrong, just follow the instruction in the status message and try again. Once the process is done, you will see a list of panels line up in the upper left corner of the screen. All the panels are given a default name. You can name them anything you want as needed. 4. Next, click "Start Pop Out". At this point, please be patient. The application will start popping out and separating panels one by one and you will see a lot of movements on screen. If something goes wrong, just follow the instruction in the status message and try again. Once the process is done, you will see a list of panels line up in the upper left corner of the screen. All the panels are given a default name. You can name them anything you want as needed.
5. You can now start panel configuration by dragging the pop out panels into their final position (to your main monitor or another monitor). You can also type value directly into the data grid to move and resize a panel. The +/- pixel buttons by the lower left corner of the grid allow you to change panel position at the chosen increment/decrement by selecting the data grid cell first (X-Pos, Y-Pos, Width, Height). You can also check "Always on Top", "Hide Title Bar", or "Full Screen Mode" if desire. If the panel is touch capable, you can check "Touch Enabled". Please see [Touch Enable Pop Out Feature](#touch-enable-pop-out-feature) regarding this experimental feature. Once all the panels are at their final position, just click "Lock Panel" to prevent further panel changes. 5. You can now start panel configuration by dragging the pop out panels into their final position (to your main monitor or another monitor). You can also type value directly into the data grid to move and resize a panel. The +/- pixel buttons by the lower left corner of the grid allow you to change panel position at the chosen increment/decrement by selecting the data grid cell first (X-Pos, Y-Pos, Width, Height). You can also check "Always on Top", "Hide Title Bar", or "Full Screen Mode" if desire. If the panel is touch capable, you can check "Touch Enabled". Please see [Touch Enable Pop Out Feature](#touch-enable-pop-out-feature) regarding this experimental feature. Once all the panels are at their final position, just click "Lock Panel" to prevent further panel changes.
<p align="center"> <p align="center">
<img src="images/doc/panel_configuration.png" width="900" hspace="10"/> <img src="images/doc/panel_configuration.png" width="900" hspace="10"/>

View file

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

View file

@ -1,16 +1,23 @@
# Version History # Version History
<hr/> <hr/>
## Version 3.3.5
* Fixed an issue when using auto pop out panel in combination with power on during cold start for the following two G1000 planes (Cessna 172 and Cessna 208B Grand Caravan), instrumentations are not powering on to allow pop out to occur.
* Fixed an issue when panels are designated as full screen mode, they're not resizing to full screen after they're popped out.
* Fixed an issue when using auto pop out panel, "Ready to Fly" button may not get click when interface scale is set to higher than 70. Unfortunately, because of how MSFS coded this particular button, this fix may add few extra seconds to the duration of auto pop out process since the application needs to search for the button to click. To speed up the pop out process, you can try to set auto pop out panel wait delays to minimum of 1 second in preferences menu and increase one second at a time until pop out works flawlessly for your system.
* Updated verbiage for "Save Auto Panning Camera" button to "Override Auto Panning Camera". This is to clear the confusion when initially selecting panels for a profile, clicking this button seems to be required. "Override Auto Panning Camera" is only needed when your camera viewport has changed for an existing profile and you do not want to recreate a new profile to set new panel locations.
* Made improvement to the behavior of Track IR (enable/disable) when using the application.
## Version 3.3.4 ## Version 3.3.4
* Fixed issue when using Auto Pop Out Panel feature in conjunction with Auto Disable Track IR setting. When performing cold start on G1000 / G1000 NXi equipped plane with Power on required checked, PFD and MFD fail to turn on or they will turn off by themselves. This resulted in pop out process to fail. * Fixed an issue when using Auto Pop Out Panel feature in conjunction with Auto Disable Track IR setting. When performing cold start on G1000 / G1000 NXi equipped plane with Power on required checked, PFD and MFD fail to turn on or they will turn off by themselves. This resulted in pop out process to fail.
* Fixed issue when using Auto Pop Out Panel and the game is in Windows mode, Pop Out Manager fails to automatically click the "Ready to Fly" button. * Fixed an issue when using Auto Pop Out Panel and the game is in Windows mode, Pop Out Manager fails to automatically click the "Ready to Fly" button.
* Made improvements to the detection of flight start and flight end. This help to resolve an issue when exiting a flight, Pop Out Manager tries to pop out panels again. * Made improvements to the detection of flight start and flight end. This help to resolve an issue when exiting a flight, Pop Out Manager tries to pop out panels again.
* Added touch enabled panel experimental feature. Please see github repo README.md on how to use this feature. This feature tries to workaround an outstanding issue regarding lack of support by MSFS with pop out that has touch component (GTN750, King Air 350, Built-in panels such as Check List, ATC, etc). * Added touch enabled panel experimental feature. Please see github repo README.md on how to use this feature. This feature tries to workaround an outstanding issue regarding lack of support by MSFS with pop out that has touch component (GTN750, King Air 350, Built-in panels such as Check List, ATC, etc).
* Updated documentations and how to videos. * Updated documentations and how to videos.
## Version 3.3.3 ## Version 3.3.3
* Fixed issue when clicking on "Show/Edit Panel Location Overlay" or setting auto TrackIR disabling option will cause PFD/MFD panels to be turned off when performing auto pop out in cold start for G1000 equipped planes. * Fixed an issue when clicking on "Show/Edit Panel Location Overlay" or setting auto Track IR disabling option will cause PFD/MFD panels to be turned off when performing auto pop out in cold start for G1000 equipped planes.
* Fixed issue where auto panning of cockpit view does not pan to previously saved camera view during pop out process. * Fixed an issue where auto panning of cockpit view does not pan to previously saved camera view during pop out process.
## Version 3.3.2 ## Version 3.3.2
* Hotfix: Fixed application crash when performing panel selections when MSFS is not running. * Hotfix: Fixed application crash when performing panel selections when MSFS is not running.

View file

@ -39,8 +39,7 @@
</Style> </Style>
</MenuItem.ItemContainerStyle> </MenuItem.ItemContainerStyle>
</MenuItem> </MenuItem>
<MenuItem Header="Show Panel Location Overlay" IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ContextMenu}, Path=DataContext.PanelSelectionViewModel.IsShownPanelCoorOverlay}" Command="{Binding Path=ShowPanelCoorOverlayCommand}"></MenuItem> <MenuItem Header="Start Pop Out" Command="{Binding Path=StartPopOutCommand}" IsEnabled="{c:Binding Path='DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0'}"></MenuItem>
<MenuItem Header="Start Pop Out" Command="{Binding Path=StartPopOutCommand}" InputGestureText="Ctrl+Alt+P"></MenuItem>
<Separator></Separator> <Separator></Separator>
<MenuItem Header="Exit" Command="{Binding Path=ExitCommand}" ></MenuItem> <MenuItem Header="Exit" Command="{Binding Path=ExitCommand}" ></MenuItem>
</ContextMenu> </ContextMenu>

View file

@ -1,19 +1,44 @@
using System; using MSFSPopoutPanelManager.Shared;
using System;
using System.Windows; using System.Windows;
namespace MSFSPopoutPanelManager.WpfApp namespace MSFSPopoutPanelManager.WpfApp
{ {
public partial class PanelCoorOverlay : Window public partial class PanelCoorOverlay : Window
{ {
private const int TOP_ADJUSTMENT = 23; // half of window height
private const int LEFT_ADJUSTMENT = 27; // half of window width
public bool IsEditingPanelLocation { get; set; } public bool IsEditingPanelLocation { get; set; }
public IntPtr WindowHandle { get; set; } public IntPtr WindowHandle { get; set; }
public event EventHandler<EventArgs<System.Drawing.Point>> WindowLocationChanged;
public PanelCoorOverlay(int panelIndex) public PanelCoorOverlay(int panelIndex)
{ {
InitializeComponent(); InitializeComponent();
this.lblPanelIndex.Content = panelIndex; this.lblPanelIndex.Content = panelIndex;
IsEditingPanelLocation = false; IsEditingPanelLocation = false;
this.LocationChanged += PanelCoorOverlay_LocationChanged;
}
public void MoveWindow(int x, int y)
{
this.Left = x - LEFT_ADJUSTMENT;
this.Top = y - TOP_ADJUSTMENT;
}
private void PanelCoorOverlay_LocationChanged(object sender, EventArgs e)
{
if (this.Top is double.NaN || this.Left is double.NaN)
return;
var top = Convert.ToInt32(this.Top);
var left = Convert.ToInt32(this.Left);
WindowLocationChanged?.Invoke(this, new EventArgs<System.Drawing.Point>(new System.Drawing.Point(left + LEFT_ADJUSTMENT, top + TOP_ADJUSTMENT)));
} }
private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) private void Window_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)

View file

@ -120,17 +120,17 @@
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.ReadyToFlyButton, Mode=TwoWay}"></mah:NumericUpDown> <mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.ReadyToFlyButton, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0">Ready to Fly</Label> <Label Margin="10,0,0,0">Ready to Fly</Label>
</WrapPanel> </WrapPanel>
<TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for 'Ready to Fly' button to appear.<LineBreak/>(Default: 6 seconds)</TextBlock> <TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for 'Ready to Fly' button to appear.<LineBreak/>(Default: 4 seconds)</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="24,0,0,0" > <WrapPanel Orientation="Horizontal" Margin="24,0,0,0" >
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView, Mode=TwoWay}"></mah:NumericUpDown> <mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0">Initial Cockpit View</Label> <Label Margin="10,0,0,0">Initial Cockpit View</Label>
</WrapPanel> </WrapPanel>
<TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for the cockpit to appear.<LineBreak/>(Default: 2 seconds)</TextBlock> <TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for the cockpit to appear.<LineBreak/>(Default: 1 second)</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="24,0,0,0" > <WrapPanel Orientation="Horizontal" Margin="24,0,0,0" >
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InstrumentationPowerOn, Mode=TwoWay}"></mah:NumericUpDown> <mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Value="{Binding Path=DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InstrumentationPowerOn, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0">Instrumentation Power On</Label> <Label Margin="10,0,0,0">Instrumentation Power On</Label>
</WrapPanel> </WrapPanel>
<TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for cold start instrumentation power on to complete.<LineBreak/>(Default: 2 seconds)</TextBlock> <TextBlock Margin="119,0,10,10" Style="{StaticResource TextBlockDescription}">Amount of time to wait for cold start instrumentation power on to complete.<LineBreak/>(Default: 1 second)</TextBlock>
<WrapPanel Orientation="Horizontal"></WrapPanel> <WrapPanel Orientation="Horizontal"></WrapPanel>
</WrapPanel> </WrapPanel>
</WrapPanel> </WrapPanel>

View file

@ -25,6 +25,14 @@
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<Style x:Key="ProfileSelectedDependencyCheckbox" TargetType="CheckBox" BasedOn="{StaticResource {x:Type CheckBox}}">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=DataStore.HasActiveUserProfileId, Mode=OneWay}" Value="True">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
<Style x:Key="ProfileAddPlaneBindingDependency" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Style x:Key="ProfileAddPlaneBindingDependency" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Width" Value="130"/> <Setter Property="Width" Value="130"/>
<Setter Property="IsEnabled" Value="False"/> <Setter Property="IsEnabled" Value="False"/>
@ -91,9 +99,7 @@
<Button Content="+" ToolTip="Add Binding" Margin="10,0,0,0" Width="40" Click="AddBinding_Click" Style="{StaticResource ProfileAddPlaneBindingDependency}"/> <Button Content="+" ToolTip="Add Binding" Margin="10,0,0,0" Width="40" Click="AddBinding_Click" Style="{StaticResource ProfileAddPlaneBindingDependency}"/>
<Button Content="-" ToolTip="Delete Binding" Margin="10,0,0,0" Width="40" Click="DeleteBinding_Click" Style="{StaticResource ProfileDeletePlaneBindingDependency}"/> <Button Content="-" ToolTip="Delete Binding" Margin="10,0,0,0" Width="40" Click="DeleteBinding_Click" Style="{StaticResource ProfileDeletePlaneBindingDependency}"/>
</WrapPanel> </WrapPanel>
<CheckBox Margin="10,5,0,0" IsChecked="{Binding Path=DataStore.ActiveUserProfile.PowerOnRequiredForColdStart}" IsEnabled="{Binding Path=DataStore.HasActiveUserProfileId}" Command="{Binding Path=SetPowerOnRequiredCommand}"> <CheckBox Margin="10,5,0,0" Content="Power on required to pop out panels on cold start (G1000 / NXi Only)" IsChecked="{Binding Path=DataStore.ActiveUserProfile.PowerOnRequiredForColdStart}" Command="{Binding Path=SetPowerOnRequiredCommand}" Style="{StaticResource ProfileSelectedDependencyCheckbox}" />
<TextBlock Text="Power on required to pop out panels on cold start (G1000 / NXi Only)" TextWrapping="Wrap" Margin="5,0,0,3"/>
</CheckBox>
</WrapPanel> </WrapPanel>
<WrapPanel Orientation="Vertical"> <WrapPanel Orientation="Vertical">
<Label Content="3. Identify pop out panel locations in the game by clicking on them." Margin="0,0,0,0" /> <Label Content="3. Identify pop out panel locations in the game by clicking on them." Margin="0,0,0,0" />
@ -112,14 +118,14 @@
</WrapPanel> </WrapPanel>
<WrapPanel Orientation="Horizontal" Margin="0,5,0,0" HorizontalAlignment="Left"> <WrapPanel Orientation="Horizontal" Margin="0,5,0,0" HorizontalAlignment="Left">
<Button Content="Start Panel Selection" HorizontalAlignment="Left" Margin="0,0,0,0" Width="165" Click="StartPanelSelection_Click" Style="{StaticResource ProfileSelectedDependency}"/> <Button Content="Start Panel Selection" HorizontalAlignment="Left" Margin="0,0,0,0" Width="165" Click="StartPanelSelection_Click" Style="{StaticResource ProfileSelectedDependency}"/>
<Button Content="Save Auto Panning Camera" HorizontalAlignment="Left" Margin="20,0,0,0" IsEnabled="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.IsFlightActive'}" Width="215" Click="SaveAutoPanningCamera_Click" Style="{StaticResource ProfileSelectedDependency}"/> <Button Content="Override Auto Panning Camera" HorizontalAlignment="Left" Margin="20,0,0,0" Width="240" Click="SaveAutoPanningCamera_Click" Style="{StaticResource ProfileSelectedDependency}"/>
</WrapPanel> </WrapPanel>
</WrapPanel> </WrapPanel>
<Separator Margin="5,10,5,5"/> <Separator Margin="5,10,5,5"/>
</WrapPanel> </WrapPanel>
<WrapPanel Orientation="Vertical" > <WrapPanel Orientation="Vertical" >
<Label Content="4. Start the pop out process for selected panels." Margin="0,0,0,0" /> <Label Content="4. Start the pop out process for selected panels." Margin="0,0,0,0" />
<Button Content="Start Pop Out" HorizontalAlignment="Left" Margin="20,5,0,0" Width="130" IsEnabled="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.IsFlightActive'}" Command="{Binding Path=StartPopOutCommand}" Style="{StaticResource ProfileSelectedDependency}"/> <Button Content="Start Pop Out" HorizontalAlignment="Left" Margin="20,5,0,0" Width="130" Click="StartPopOut_Click" Style="{StaticResource ProfileSelectedDependency}"/>
</WrapPanel> </WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="Visible"> <WrapPanel Orientation="Vertical" Visibility="Visible">
</WrapPanel> </WrapPanel>
@ -160,7 +166,7 @@
<DataGridTextColumn Header="Y-Pos" Width="97" Binding="{Binding Y}"/> <DataGridTextColumn Header="Y-Pos" Width="97" Binding="{Binding Y}"/>
</DataGrid.Columns> </DataGrid.Columns>
</DataGrid> </DataGrid>
<CheckBox DockPanel.Dock="Bottom" Content="Show/Edit Panel Location Overlay" HorizontalAlignment="Center" Command="{Binding Path=EditPanelCoorOverlayCommand}" IsChecked="{Binding Path=IsEditingPanelCoorOverlay, Mode=TwoWay}"/> <CheckBox DockPanel.Dock="Bottom" Content="Show/Edit Panel Location Overlay" HorizontalAlignment="Center" Command="{Binding Path=EditPanelCoorOverlayCommand}" IsChecked="{Binding Path=IsEditingPanelCoorOverlay, Mode=TwoWay}" Style="{StaticResource ProfileSelectedDependencyCheckbox}"/>
</DockPanel> </DockPanel>
</DockPanel> </DockPanel>
</Grid> </Grid>

View file

@ -89,6 +89,14 @@ namespace MSFSPopoutPanelManager.WpfApp
} }
} }
private void StartPopOut_Click(object sender, RoutedEventArgs e)
{
if (_panelSelectionViewModel.DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0)
{
_panelSelectionViewModel.StartPopOutCommand.Execute(null);
}
}
private void AddBinding_Click(object sender, RoutedEventArgs e) private void AddBinding_Click(object sender, RoutedEventArgs e)
{ {
_panelSelectionViewModel.AddProfileBindingCommand.Execute(null); _panelSelectionViewModel.AddProfileBindingCommand.Execute(null);

View file

@ -49,7 +49,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
public DelegateCommand UserGuideCommand => new DelegateCommand((o) => { DiagnosticManager.OpenOnlineUserGuide(); }, CanExecute); public DelegateCommand UserGuideCommand => new DelegateCommand((o) => { DiagnosticManager.OpenOnlineUserGuide(); }, CanExecute);
public DelegateCommand DownloadLatestReleaseCommand => new DelegateCommand((o) => { DiagnosticManager.OpenOnlineLatestDownload(); }, CanExecute); public DelegateCommand DownloadLatestReleaseCommand => new DelegateCommand((o) => { DiagnosticManager.OpenOnlineLatestDownload(); }, CanExecute);
public DelegateCommand UserProfileSelectCommand => new DelegateCommand(OnUserProfileSelected, CanExecute); public DelegateCommand UserProfileSelectCommand => new DelegateCommand(OnUserProfileSelected, CanExecute);
public DelegateCommand ShowPanelCoorOverlayCommand => new DelegateCommand(OnShowPanelCoorOverlay, CanExecute);
public DelegateCommand StartPopOutCommand => new DelegateCommand(OnStartPopOut, CanExecute); public DelegateCommand StartPopOutCommand => new DelegateCommand(OnStartPopOut, CanExecute);
public ApplicationViewModel() public ApplicationViewModel()
@ -98,7 +97,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
PanelConfigurationViewModel = new PanelConfigurationViewModel(DataStore, _userProfileManager); PanelConfigurationViewModel = new PanelConfigurationViewModel(DataStore, _userProfileManager);
PreferencesViewModel = new PreferencesViewModel(DataStore); PreferencesViewModel = new PreferencesViewModel(DataStore);
InputHookManager.OnStartPopout += (source, e) => { OnStartPopOut(null); };
} }
public void Initialize() public void Initialize()
@ -137,8 +135,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
ShowPanelSelection(true); ShowPanelSelection(true);
IsMinimizedAllPanels = false; IsMinimizedAllPanels = false;
InputHookManager.StartHook();
} }
public void Exit() public void Exit()
@ -146,6 +142,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
// This method gets call on Windows_Closing // This method gets call on Windows_Closing
InputHookManager.EndHook(); InputHookManager.EndHook();
_simConnectManager.Stop(); _simConnectManager.Stop();
Application.Current.Shutdown();
} }
private void OnRestart(object commandParameter) private void OnRestart(object commandParameter)
@ -209,12 +206,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
DataStore.ActiveUserProfileId = profileId; DataStore.ActiveUserProfileId = profileId;
} }
private void OnShowPanelCoorOverlay(object commandParameter)
{
PanelSelectionViewModel.IsEditingPanelCoorOverlay = !PanelSelectionViewModel.IsEditingPanelCoorOverlay;
PanelSelectionViewModel.EditPanelCoorOverlayCommand.Execute(null);
}
private void OnStartPopOut(object commandParameter) private void OnStartPopOut(object commandParameter)
{ {
ShowPanelSelection(true); ShowPanelSelection(true);

View file

@ -19,7 +19,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
_activeProfileId = -1; _activeProfileId = -1;
_allowEdit = true; _allowEdit = true;
IsFlightActive = true; // ToDo: temporary for testing
} }
public AppSetting AppSetting public AppSetting AppSetting
@ -39,8 +38,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
} }
// bubble event up to this 'DataStore' level // bubble event up to this 'DataStore' level
_appSetting.AutoPopOutPanelsChanged += (sender, e) => _appSetting.AutoPopOutPanelsChanged += (sender, e) =>
{ {
IsEnableAutoPopOutPanel = e.Value; IsEnableAutoPopOutPanel = e.Value;
@ -183,7 +180,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
public bool IsEnteredFlight { get; set; } public bool IsEnteredFlight { get; set; }
public bool IsFlightActive { get; set; }
public bool IsEnableAutoPopOutPanel { get; set; } public bool IsEnableAutoPopOutPanel { get; set; }
} }
} }

View file

@ -6,7 +6,6 @@ using System;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Interop;
namespace MSFSPopoutPanelManager.WpfApp.ViewModel namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
@ -42,8 +41,10 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
DataStore = dataStore; DataStore = dataStore;
DataStore.OnActiveUserProfileChanged += (sender, e) => DataStore.OnActiveUserProfileChanged += (sender, e) =>
{ {
IsEditingPanelCoorOverlay = false; if (IsEditingPanelCoorOverlay && DataStore.AppSetting.AutoDisableTrackIR)
RemoveAllPanelCoorOverlay(); _simConnectManager.TurnOnTrackIR();
ShowPanelOverlay(false);
}; };
_userProfileManager = userProfileManager; _userProfileManager = userProfileManager;
@ -124,6 +125,8 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
if (DataStore.AppSetting.AutoDisableTrackIR) if (DataStore.AppSetting.AutoDisableTrackIR)
_simConnectManager.TurnOffTrackIR(); _simConnectManager.TurnOffTrackIR();
RemoveAllPanelCoorOverlay();
WindowManager.MinimizeWindow(DataStore.ApplicationHandle); // Window hide doesn't work when try to reshow window after selection completes. So need to use minimize. WindowManager.MinimizeWindow(DataStore.ApplicationHandle); // Window hide doesn't work when try to reshow window after selection completes. So need to use minimize.
_panelSelectionManager.UserProfile = DataStore.ActiveUserProfile; _panelSelectionManager.UserProfile = DataStore.ActiveUserProfile;
_panelSelectionManager.AppSetting = DataStore.AppSetting; _panelSelectionManager.AppSetting = DataStore.AppSetting;
@ -134,16 +137,27 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
Thread.Sleep(500); // allow time for the mouse to be stopped moving by the user Thread.Sleep(500); // allow time for the mouse to be stopped moving by the user
var simulatorProcess = DiagnosticManager.GetSimulatorProcess(); ShowPanelOverlay(false);
InputHookManager.EndHook();
if (!(DataStore.IsSimulatorStarted && DataStore.IsFlightActive)) if (!(DataStore.IsSimulatorStarted))
{ {
Logger.LogStatus("MSFS/SimConnect has not been started. Please try again at a later time.", StatusMessageType.Error); Logger.LogStatus("MSFS/SimConnect has not been started. Please try again at a later time.", StatusMessageType.Error);
return; return;
} }
if(DataStore.ActiveUserProfile.PanelSourceCoordinates.Count == 0)
{
Logger.LogStatus("No panel has been selected for the profile. Please select at least one panel.", StatusMessageType.Error);
return;
}
if (DataStore.ActiveUserProfile != null && DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0) if (DataStore.ActiveUserProfile != null && DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0)
{ {
// Turn off TrackIR if TrackIR is started
if (DataStore.AppSetting.AutoDisableTrackIR)
_simConnectManager.TurnOffTrackIR();
Logger.LogStatus("Panels pop out in progress.....", StatusMessageType.Info); Logger.LogStatus("Panels pop out in progress.....", StatusMessageType.Info);
var messageDialog = new OnScreenMessageDialog($"Panels pop out in progress for profile:\n{DataStore.ActiveUserProfile.ProfileName}", DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView); var messageDialog = new OnScreenMessageDialog($"Panels pop out in progress for profile:\n{DataStore.ActiveUserProfile.ProfileName}", DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView);
@ -165,17 +179,20 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
private void OnSaveAutoPanningCamera(object commandParameter) private void OnSaveAutoPanningCamera(object commandParameter)
{ {
var simualatorProcess = DiagnosticManager.GetSimulatorProcess(); var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null && DataStore.IsFlightActive) if (simualatorProcess == null)
{ {
InputEmulationManager.SaveCustomView(simualatorProcess.Handle, DataStore.AppSetting.AutoPanningKeyBinding); Logger.LogStatus("MSFS/SimConnect has not been started. Please try again at a later time.", StatusMessageType.Error);
Logger.LogStatus("Auto Panning Camera has been saved succesfully.", StatusMessageType.Info); return;
} }
InputEmulationManager.SaveCustomView(simualatorProcess.Handle, DataStore.AppSetting.AutoPanningKeyBinding);
Logger.LogStatus("Auto Panning Camera has been saved succesfully.", StatusMessageType.Info);
} }
private void OnEditPanelCoorOverlay(object commandParameter) private void OnEditPanelCoorOverlay(object commandParameter)
{ {
// Turn off TrackIR if TrackIR is started // Turn off TrackIR if TrackIR is started
if (commandParameter == null && DataStore.AppSetting.AutoDisableTrackIR) if (DataStore.AppSetting.AutoDisableTrackIR)
{ {
if (IsEditingPanelCoorOverlay) if (IsEditingPanelCoorOverlay)
_simConnectManager.TurnOffTrackIR(); _simConnectManager.TurnOffTrackIR();
@ -185,17 +202,19 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
if (IsEditingPanelCoorOverlay) if (IsEditingPanelCoorOverlay)
{ {
RemoveAllPanelCoorOverlay(); ShowPanelOverlay(true);
DataStore.ActiveUserProfile.PanelSourceCoordinates.ToList().ForEach(c => AddPanelCoorOverlay(c));
_panelSelectionManager.UserProfile = DataStore.ActiveUserProfile; _panelSelectionManager.UserProfile = DataStore.ActiveUserProfile;
_panelSelectionManager.AppSetting = DataStore.AppSetting; _panelSelectionManager.AppSetting = DataStore.AppSetting;
_panelSelectionManager.StartEditPanelLocations();
if (DataStore.AppSetting.UseAutoPanning)
InputEmulationManager.LoadCustomView(DataStore.AppSetting.AutoPanningKeyBinding);
InputHookManager.StartHook();
} }
else else
{ {
_panelSelectionManager.EndEditPanelLocations(); InputHookManager.EndHook();
RemoveAllPanelCoorOverlay(); ShowPanelOverlay(false);
} }
} }
@ -203,18 +222,16 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
PanelCoorOverlay overlay = new PanelCoorOverlay(panelSourceCoordinate.PanelIndex); PanelCoorOverlay overlay = new PanelCoorOverlay(panelSourceCoordinate.PanelIndex);
overlay.IsEditingPanelLocation = IsEditingPanelCoorOverlay; overlay.IsEditingPanelLocation = IsEditingPanelCoorOverlay;
overlay.Loaded += (sender, e) => overlay.WindowStartupLocation = WindowStartupLocation.Manual;
{ overlay.MoveWindow(panelSourceCoordinate.X, panelSourceCoordinate.Y);
var overlay = (Window)sender;
var handle = new WindowInteropHelper(Window.GetWindow(overlay)).Handle;
panelSourceCoordinate.PanelHandle = handle;
WindowManager.MoveWindow(handle, PanelType.WPFWindow, (int)overlay.Left, (int)overlay.Top, (int)overlay.Width, (int)overlay.Height);
};
overlay.WindowStartupLocation = System.Windows.WindowStartupLocation.Manual;
overlay.Left = panelSourceCoordinate.X - overlay.Width / 2;
overlay.Top = panelSourceCoordinate.Y - overlay.Height / 2;
overlay.ShowInTaskbar = false; overlay.ShowInTaskbar = false;
overlay.Show(); overlay.Show();
overlay.WindowLocationChanged += (sender, e) =>
{
panelSourceCoordinate.X = e.Value.X;
panelSourceCoordinate.Y = e.Value.Y;
_userProfileManager.WriteUserProfiles();
};
} }
private void RemoveLastAddedPanelCoorOverlay() private void RemoveLastAddedPanelCoorOverlay()
@ -243,8 +260,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
private void HandleOnPopOutStarted(object sender, EventArgs e) private void HandleOnPopOutStarted(object sender, EventArgs e)
{ {
// Hide panel coordinate overlays // Hide panel coordinate overlays
IsEditingPanelCoorOverlay = false; ShowPanelOverlay(false);
OnEditPanelCoorOverlay(false);
// Close all pop out panels // Close all pop out panels
WindowManager.CloseAllCustomPopoutPanels(); WindowManager.CloseAllCustomPopoutPanels();
@ -275,7 +291,6 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
OnPopOutCompleted?.Invoke(this, null); OnPopOutCompleted?.Invoke(this, null);
} }
if (DataStore.AppSetting.AutoDisableTrackIR) if (DataStore.AppSetting.AutoDisableTrackIR)
_simConnectManager.TurnOnTrackIR(); _simConnectManager.TurnOnTrackIR();
} }
@ -285,23 +300,27 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
WindowManager.BringWindowToForeground(DataStore.ApplicationHandle); WindowManager.BringWindowToForeground(DataStore.ApplicationHandle);
DataStore.ApplicationWindow.Show(); DataStore.ApplicationWindow.Show();
IsEditingPanelCoorOverlay = true;
OnEditPanelCoorOverlay(null);
if (DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0) if (DataStore.ActiveUserProfile.PanelSourceCoordinates.Count > 0)
Logger.LogStatus("Panels selection is completed. Please click 'Start Pop Out' to start popping out these panels.", StatusMessageType.Info); Logger.LogStatus("Panels selection is completed. Please click 'Start Pop Out' to start popping out these panels.", StatusMessageType.Info);
else else
Logger.LogStatus("Panels selection is completed. No panel has been selected.", StatusMessageType.Info); Logger.LogStatus("Panels selection is completed. No panel has been selected.", StatusMessageType.Info);
IsEditingPanelCoorOverlay = true;
if (DataStore.AppSetting.AutoDisableTrackIR)
_simConnectManager.TurnOnTrackIR();
} }
private bool CanExecute(object commandParameter) private bool CanExecute(object commandParameter)
{ {
return true; return true;
} }
private void ShowPanelOverlay(bool show)
{
IsEditingPanelCoorOverlay = show;
RemoveAllPanelCoorOverlay();
if (show && DataStore.ActiveUserProfile != null)
DataStore.ActiveUserProfile.PanelSourceCoordinates.ToList().ForEach(c => AddPanelCoorOverlay(c));
}
} }
public class AddProfileCommandParameter public class AddProfileCommandParameter

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.3.4.0</Version> <Version>3.3.5.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>
@ -49,7 +49,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Autoupdater.NET.Official" Version="1.7.3" /> <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="CommunityToolkit.Mvvm" Version="7.1.2" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="7.1.2" />
<PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" /> <PackageReference Include="Hardcodet.NotifyIcon.Wpf" Version="1.1.0" />

View file

@ -1,19 +1,26 @@
Version 3.3.4.0 Version 3.3.5.0
- Fixed issue when using Auto Pop Out Panel feature in conjunction with Auto Disable This update is optional. You can safely skip this version if you do not require the following
Track IR setting. When performing cold start on G1000 / G1000 NXi equipped plane with application fixes.
Power on required checked, PFD and MFD fail to turn on or they will turn off by themselves.
This resulted in pop out process to fail.
- Fixed issue when using Auto Pop Out Panel and the game is in Windows mode, Pop Out Manager - Fixed an issue when using auto pop out panel in combination with power on during cold start for
fails to automatically click the "Ready to Fly" button. the following two G1000 planes (Cessna 172 and Cessna 208B Grand Caravan), instrumentations are
not powering on to allow pop out to occur.
- Made improvements to the detection of flight starts and flight ends. This help to resolve an - Fixed an issue when panels are designated as full screen mode, they're not resizing to full
issue when exiting a flight, Pop Out Manager tries to pop out panels again. screen after they're popped out.
- Added touch enabled panel experimental feature. Please see github repo README.md on - Fixed an issue when using auto pop out panel, "Ready to Fly" button may not get click when
how to use this feature. This feature tries to workaround an outstanding issue regarding lack interface scale is set to higher than 70. Unfortunately, because of how MSFS coded this
of support by MSFS with pop out that has touch component (GTN750, King Air 350, Built-in panels particular button, this fix may add few extra seconds to the duration of auto pop out process
such as Check List, ATC, etc). since the application needs to search for the button to click. To speed up the pop out process,
you can try to set auto pop out panel wait delays to minimum of 1 second in preferences menu and
increase one second at a time until pop out works flawlessly for your system.
- Updated documentation and how to videos. - Updated verbiage for "Save Auto Panning Camera" button to "Override Auto Panning Camera". This
is to clear the confusion when initially selecting panels for a profile, clicking this button
eems to be required. "Override Auto Panning Camera" is only needed when your camera viewport has
changed for an existing profile and you do not want to recreate a new profile to set new panel
locations.
- Made improvement to the behavior of Track IR (enable/disable) when using the application.