diff --git a/MSFSPopoutPanelManager.csproj b/MSFSPopoutPanelManager.csproj
index 3bea90a..2d87d85 100644
--- a/MSFSPopoutPanelManager.csproj
+++ b/MSFSPopoutPanelManager.csproj
@@ -5,7 +5,7 @@
net5.0-windows
true
x64;AnyCPU
- 3.0
+ 3.0.1
MSFSPopoutPanelManager
MSFSPopoutPanelManager
WindowManager.ico
@@ -13,8 +13,8 @@
MSFS 2020 Popout Panel Manager
MSFS 2020 Popout Panel Manager
true
- 3.0.0.0
- 3.0.0.0
+ 3.0.1.0
+ 3.0.1.0
diff --git a/Provider/PopoutSeparationManager.cs b/Provider/PopoutSeparationManager.cs
index 749c243..2886fcd 100644
--- a/Provider/PopoutSeparationManager.cs
+++ b/Provider/PopoutSeparationManager.cs
@@ -149,6 +149,12 @@ namespace MSFSPopoutPanelManager.Provider
// Add the built-in pop outs (ie. ATC, VFR Map) to the panel list
PInvoke.EnumWindows(new PInvoke.CallBack(EnumBuiltinPopoutCallBack), _profile.PanelSourceCoordinates.Count + 1);
+ // Add the MSFS Touch Panel (My other github project) windows to the panel list
+ PInvoke.EnumWindows(new PInvoke.CallBack(EnumMSFSTouchPanelPopoutCallBack), _profile.PanelSourceCoordinates.Count + 1);
+
+ if (_panels.Count == 0)
+ throw new PopoutManagerException("No panels have been found. Please select or open at least one in-game panel or MSFS Touch Panel App's panel.");
+
// Line up all the panels and fill in meta data
for (var i = _panels.Count - 1; i >= 0; i--)
{
@@ -158,7 +164,7 @@ namespace MSFSPopoutPanelManager.Provider
_panels[i].Width = 800;
_panels[i].Height = 600;
- PInvoke.MoveWindow(_panels[i].PanelHandle, -8 + _panels[i].Top, _panels[i].Left, _panels[i].Width, _panels[i].Height, true);
+ PInvoke.MoveWindow(_panels[i].PanelHandle, _panels[i].Top, _panels[i].Left, _panels[i].Width, _panels[i].Height, true);
PInvoke.SetForegroundWindow(_panels[i].PanelHandle);
Thread.Sleep(200);
}
@@ -295,6 +301,22 @@ namespace MSFSPopoutPanelManager.Provider
return true;
}
+ public bool EnumMSFSTouchPanelPopoutCallBack(IntPtr hwnd, int index)
+ {
+ var panelInfo = GetPanelWindowInfo(hwnd);
+
+ if (panelInfo != null && panelInfo.PanelType == PanelType.MSFSTouchPanel)
+ {
+ if (!_panels.Exists(x => x.PanelHandle == hwnd))
+ {
+ panelInfo.PanelIndex = index;
+ _panels.Add(panelInfo);
+ }
+ }
+
+ return true;
+ }
+
private PanelConfig GetPanelWindowInfo(IntPtr hwnd)
{
var className = PInvoke.GetClassName(hwnd);
@@ -316,6 +338,22 @@ namespace MSFSPopoutPanelManager.Provider
return panelInfo;
}
+ else // For MSFS Touch Panel window
+ {
+ var caption = PInvoke.GetWindowText(hwnd);
+
+ var panelInfo = new PanelConfig();
+ panelInfo.PanelHandle = hwnd;
+ panelInfo.PanelName = caption;
+
+ if (caption.IndexOf("MSFS Touch Panel |") > -1)
+ {
+ panelInfo.PanelType = PanelType.MSFSTouchPanel;
+ return panelInfo;
+ }
+ else
+ return null;
+ }
return null;
}
diff --git a/Shared/Enums.cs b/Shared/Enums.cs
index cb098da..0604703 100644
--- a/Shared/Enums.cs
+++ b/Shared/Enums.cs
@@ -4,6 +4,7 @@
{
FlightSimMainWindow,
BuiltInPopout,
- CustomPopout
+ CustomPopout,
+ MSFSTouchPanel
}
}
diff --git a/UI/StartupForm.cs b/UI/StartupForm.cs
index dbab7f6..ddf8d40 100644
--- a/UI/StartupForm.cs
+++ b/UI/StartupForm.cs
@@ -31,7 +31,7 @@ namespace MSFSPopoutPanelManager.UI
panelSteps.Controls.Add(_ucPanelConfiguration);
// Set version number
- lblVersion.Text += System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
+ lblVersion.Text += $"{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Major}.{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Minor}.{System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.Build}";
_controller = new StartUpController(this);
_controller.OnSimConnectionChanged += HandleSimConnectionChanged;
diff --git a/UI/UserControlPanelConfiguration.cs b/UI/UserControlPanelConfiguration.cs
index 4c12bac..74f70e2 100644
--- a/UI/UserControlPanelConfiguration.cs
+++ b/UI/UserControlPanelConfiguration.cs
@@ -22,7 +22,7 @@ namespace MSFSPopoutPanelManager.UI
dataGridViewPanels.DataSource = _controller.PanelConfigs;
dataGridViewPanels.CellBeginEdit += HandleCellBeginEdit;
dataGridViewPanels.CellValidating += HandleCellValidating;
- dataGridViewPanels.CellValueChanged += HandleCellValueChanged;
+ dataGridViewPanels.CellEndEdit += HandleCellValueChanged;
dataGridViewPanels.CellContentClick += HandleCellValueChanged;
buttonSaveSettings.Click += (source, e) => { dataGridViewPanels.EndEdit(); _controller.SaveSettings(); };
diff --git a/UIController/PanelConfigurationController.cs b/UIController/PanelConfigurationController.cs
index c9b8e37..c4dcf67 100644
--- a/UIController/PanelConfigurationController.cs
+++ b/UIController/PanelConfigurationController.cs
@@ -10,7 +10,8 @@ namespace MSFSPopoutPanelManager.UIController
public class PanelConfigurationController : BaseController
{
private const int WINEVENT_OUTOFCONTEXT = 0;
- private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
+ //private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
+ private const uint EVENT_OBJECT_LOCATIONCHANGE = 0x800B;
private static PInvoke.WinEventProc _winEvent; // keep this as static to prevent garbage collect or the app will crash
private IntPtr _winEventHook;
@@ -52,36 +53,33 @@ namespace MSFSPopoutPanelManager.UIController
public void CellValueChanged(int rowIndex, PanelConfigDataColumn column, object newCellValue)
{
+ int orignalLeft = PanelConfigs[rowIndex].Left;
+
if (rowIndex != -1)
{
switch (column)
{
case PanelConfigDataColumn.PanelName:
- PanelConfigs[rowIndex].PanelName = Convert.ToString(newCellValue);
PInvoke.SetWindowText(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].PanelName);
break;
case PanelConfigDataColumn.Left:
- PanelConfigs[rowIndex].Left = Convert.ToInt32(newCellValue);
PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, true);
break;
case PanelConfigDataColumn.Top:
- PanelConfigs[rowIndex].Top = Convert.ToInt32(newCellValue);
PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, true);
break;
case PanelConfigDataColumn.Width:
- PanelConfigs[rowIndex].Width = Convert.ToInt32(newCellValue);
PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, true);
+ MSFSBugPanelShiftWorkaround(PanelConfigs[rowIndex].PanelHandle, orignalLeft, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height);
break;
case PanelConfigDataColumn.Height:
- PanelConfigs[rowIndex].Height = Convert.ToInt32(newCellValue);
PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, true);
+ MSFSBugPanelShiftWorkaround(PanelConfigs[rowIndex].PanelHandle, orignalLeft, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height);
break;
case PanelConfigDataColumn.AlwaysOnTop:
- PanelConfigs[rowIndex].AlwaysOnTop = Convert.ToBoolean(newCellValue);
WindowManager.ApplyAlwaysOnTop(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].AlwaysOnTop, new Rectangle(PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height));
break;
case PanelConfigDataColumn.HideTitlebar:
- PanelConfigs[rowIndex].HideTitlebar = Convert.ToBoolean(newCellValue);
WindowManager.ApplyHidePanelTitleBar(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].HideTitlebar);
break;
default:
@@ -92,64 +90,89 @@ namespace MSFSPopoutPanelManager.UIController
public void CellValueIncrDecr(int rowIndex, PanelConfigDataColumn column, int changeAmount)
{
+ int orignalLeft = PanelConfigs[rowIndex].Left;
+
if (rowIndex != -1)
{
switch (column)
{
case PanelConfigDataColumn.Left:
- PanelConfigs[rowIndex].Left += changeAmount;
+ PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left + changeAmount, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, false);
break;
case PanelConfigDataColumn.Top:
- PanelConfigs[rowIndex].Top += changeAmount;
+ PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top + changeAmount, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, false);
break;
case PanelConfigDataColumn.Width:
- PanelConfigs[rowIndex].Width += changeAmount;
+ PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width + changeAmount, PanelConfigs[rowIndex].Height, false);
+ MSFSBugPanelShiftWorkaround(PanelConfigs[rowIndex].PanelHandle, orignalLeft, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width + changeAmount, PanelConfigs[rowIndex].Height);
break;
case PanelConfigDataColumn.Height:
- PanelConfigs[rowIndex].Height += changeAmount;
+ PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height + changeAmount, false);
+ MSFSBugPanelShiftWorkaround(PanelConfigs[rowIndex].PanelHandle, orignalLeft, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height + changeAmount);
break;
default:
return;
}
-
- RefreshDataUI?.Invoke(this, null);
- PInvoke.MoveWindow(PanelConfigs[rowIndex].PanelHandle, PanelConfigs[rowIndex].Left, PanelConfigs[rowIndex].Top, PanelConfigs[rowIndex].Width, PanelConfigs[rowIndex].Height, true);
}
}
+ private void MSFSBugPanelShiftWorkaround(IntPtr handle, int originalLeft, int top, int width, int height)
+ {
+ // Fixed MSFS bug, create workaround where on 2nd or later instance of width adjustment, the panel shift to the left by itself
+ // Wait for system to catch up on panel coordinate that were just applied
+ System.Threading.Thread.Sleep(200);
+
+ Rectangle rectangle;
+ PInvoke.GetWindowRect(handle, out rectangle);
+
+ if (rectangle.Left != originalLeft)
+ PInvoke.MoveWindow(handle, originalLeft, top, width, height, false);
+ }
+
private void HandlePopOutCompleted(object sender, EventArgs e)
{
// Populate panel data
BaseController.ActiveUserPlaneProfile.PanelConfigs.ForEach(p => PanelConfigs.Add(p));
// Setup panel config event hooks
- _winEventHook = PInvoke.SetWinEventHook(EVENT_SYSTEM_MOVESIZEEND, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, _winEvent, 0, 0, WINEVENT_OUTOFCONTEXT);
+ _winEventHook = PInvoke.SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, IntPtr.Zero, _winEvent, 0, 0, WINEVENT_OUTOFCONTEXT);
+
}
+ private Rectangle _lastWindowRectangle;
+
private void EventCallback(IntPtr hWinEventHook, uint iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime)
{
+ if (hWnd == IntPtr.Zero) return;
+
var panelConfig = PanelConfigs.FirstOrDefault(panel => panel.PanelHandle == hWnd);
if (panelConfig != null)
{
var rowIndex = PanelConfigs.IndexOf(panelConfig);
- HightlightSelectedPanel?.Invoke(this, new EventArgs(rowIndex));
-
if (panelConfig != null)
{
switch (iEvent)
{
- case EVENT_SYSTEM_MOVESIZEEND:
+ case EVENT_OBJECT_LOCATIONCHANGE:
Rectangle winRectangle;
- Rectangle clientRectangle;
PInvoke.GetWindowRect(panelConfig.PanelHandle, out winRectangle);
+
+ if (_lastWindowRectangle == winRectangle) // ignore duplicate callback messages
+ return;
+
+ _lastWindowRectangle = winRectangle;
+ Rectangle clientRectangle;
PInvoke.GetClientRect(panelConfig.PanelHandle, out clientRectangle);
- panelConfig.Top = winRectangle.Top;
panelConfig.Left = winRectangle.Left;
+ panelConfig.Top = winRectangle.Top;
panelConfig.Width = clientRectangle.Width + 16;
panelConfig.Height = clientRectangle.Height + 39;
+
+ HightlightSelectedPanel?.Invoke(this, new EventArgs(rowIndex));
+
break;
}
diff --git a/UIController/PanelSelectionController.cs b/UIController/PanelSelectionController.cs
index 6ae7cf8..9c307c7 100644
--- a/UIController/PanelSelectionController.cs
+++ b/UIController/PanelSelectionController.cs
@@ -140,10 +140,10 @@ namespace MSFSPopoutPanelManager.UIController
_panelSelectionManager.ShowPanelLocationOverlay(false);
ShowPanelLocationOverlay = false;
- if (PanelCoordinates.Count == 0)
- OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.ProfileSelected));
- else
- OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.PanelSelectionCompletedValid));
+ //if (PanelCoordinates.Count == 0)
+ // OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.ProfileSelected));
+ //else
+ OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.PanelSelectionCompletedValid));
}
}
@@ -255,13 +255,13 @@ namespace MSFSPopoutPanelManager.UIController
if (_parentForm != null)
_parentForm.WindowState = FormWindowState.Normal;
- if (PanelCoordinates.Count > 0)
- {
+ //if (PanelCoordinates.Count > 0)
+ //{
ActiveUserPlaneProfile.PanelSourceCoordinates = PanelCoordinates.ToList();
OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.PanelSelectionCompletedValid));
- }
- else
- OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.PanelSelectionCompletedInvalid));
+ //}
+ //else
+ // OnUIStateChanged?.Invoke(this, new EventArgs(PanelSelectionUIState.PanelSelectionCompletedInvalid));
ShowPanelLocationOverlay = true;
diff --git a/VERSION.md b/VERSION.md
index 708e6e2..46c830d 100644
--- a/VERSION.md
+++ b/VERSION.md
@@ -1,7 +1,15 @@
# Version History
-## Version 3.0.0.0
+## Version 3.0.1
+* Added workaround for MSFS pop out panel adjustment bug so using the position data grid to adjust width and height will work as expected.
+ - In MSFS, when changing height or width of a pop out panel (2nd time for same panel and onward), there is an MSFS bug that will unexpectedly shifted the panel by 8px to the left for each adjustment.
+* Improved realtime feedback of panel's current coordiates as you move panels around or adjust the top, left, height and width of panel.
+* Fixed always on top issue when moving panel around for placement inside other overlay or bezel.
+* Added support to create profile just to save locations of built-in panels only (VFR, ATC, etc).
+* Added support to save locations for web panels from my other github project "MSFS Touch Panel".
+
+## Version 3.0.0
* Provided 2X pop out and panel separation performance.
* Better support for all screen resolutions.
* Added Cold Start feature. Panels can be popped out and recalled later even when they're not turned on.
@@ -11,7 +19,7 @@
* Added realtime readout during panel positioning.
* Added exception tracing to help troubleshoot application issue.
-## Vesion 2.2.0.0
+## Vesion 2.2.0
* Disabled ability to launch multiple instances of the application.
* Added autostart feature when MSFS starts. The application will create or modify exe.xml. A backup copy of exe.xml will be created.
* Added better support for 4K display resolution and non-standard display resolution.
@@ -19,32 +27,32 @@
* Improved panel pop out separation accuracy and performance.
* Updated application packaging to single file executable to reduce file clutter.
-## Vesion 2.1.1.0
+## Vesion 2.1.1
* Fixed panel separation issue for super ultrawide monitor (for example: 3840x1080)
-## Vesion 2.1.0.0
+## Vesion 2.1.0
* Added ability to delete built-in profile.
* Added ability to create and delete custom user profile.
* Improved image recognition algorithm using SUSAN Corner block matching algorithm.
-## Vesion 2.0.3.0
+## Vesion 2.0.3
* Fixed a crash bug when splitting out panel when trying to analyze the last split panel.
* Added PMS50.com GTN750 mod configuration
-## Vesion 2.0.2.0
+## Vesion 2.0.2
* Added one second delay on mouse click when the application is trying to separate the chained pop out windows.
-## Vesion 2.0.1.0
+## Vesion 2.0.1
* Changed how screen resolution is detected. Used vertical instead of horizontal resolution to account for ultra wide monitors.
-## Version 2.0.0.0
+## Version 2.0.0
* Used new image recognition instead of OCR technology to determine pop outs.
* Added auto pop out feature.
* Allowed moving pop out panels using coordinates/width/height after analysis.
* Added additional plane profiles.
* Running on non-native monitor resolution will not work because of image scaling issue when doing image analysis.
-## Version 1.2.0.0
+## Version 1.2.0
* Increase OCR image accuracy by raising image DPI before analysis.
* Added (very experimental) Asobo A320 and FlybyWire A320NX profiles as testing sample. These profiles do only work 100% of the time. Continue investigation into better OCR accuracy will be needed.
* Added profile dropdown sorted by profile name.
@@ -54,7 +62,7 @@
* Fixed application path issue for not able to find ocrdata.json file at startup.
* Removed MSFS Pop Out Panel Manager is always on top. This is intefering with image operations.
-## Version 1.1.0.0
+## Version 1.1.0
* Added caption title for the "untitled" windows. After analysis, if the panel window matches the name in the profile/ocr definition file, it will now display a caption of "Custom - XXXXX" (ie. Custom - PFD). This allows user to use various 3rd party windows layout manager to organize pop out panel windows.
* Added hide panel title bar feature.
* Added ability to have pop out panels to be always on top.
@@ -62,5 +70,5 @@
* Made application flow more intuitive.
* Fixed various small bugs in the application.
-## Version 1.0.0.0
+## Version 1.0.0
* Initial Release
\ No newline at end of file