From 0a3d4853638f33713826e1af7573b4749b2d1e0e Mon Sep 17 00:00:00 2001 From: hawkeye Date: Mon, 20 Dec 2021 09:57:33 -0500 Subject: [PATCH] Version 3.0.1 --- MSFSPopoutPanelManager.csproj | 6 +- Provider/PopoutSeparationManager.cs | 40 +++++++++++- Shared/Enums.cs | 3 +- UI/StartupForm.cs | 2 +- UI/UserControlPanelConfiguration.cs | 2 +- UIController/PanelConfigurationController.cs | 65 +++++++++++++------- UIController/PanelSelectionController.cs | 18 +++--- VERSION.md | 30 +++++---- 8 files changed, 118 insertions(+), 48 deletions(-) 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