1
0
Fork 0
mirror of https://github.com/hawkeye-stan/msfs-popout-panel-manager.git synced 2024-10-16 06:00:45 +00:00

v1.2.0.0 updates

This commit is contained in:
hawkeye 2021-09-23 09:34:10 -04:00
parent d4c4203931
commit bba7331dbb
7 changed files with 182 additions and 63 deletions

View file

@ -51,7 +51,7 @@ namespace MSFSPopoutPanelManager
public event EventHandler<EventArgs<string>> OnStatusUpdated;
public event EventHandler<EventArgs<Dictionary<string, string>>> OnOcrDebugged;
public void Analyze(ref MainWindow simWindow, string profile)
public void Analyze(ref MainWindow simWindow, OcrEvalData ocrEvaluationData)
{
MainWindow processZeroMainWindow = null;
@ -107,7 +107,6 @@ namespace MSFSPopoutPanelManager
if(simWindow.ChildWindowsData.Count > 0)
{
var ocrEvaluationData = FileManager.ReadProfileData().Find(x => x.Profile == profile);
var ocrImageScale = ocrEvaluationData.OCRImageScale;
Dictionary<string, string> debugInfo = new Dictionary<string, string>();
@ -133,7 +132,7 @@ namespace MSFSPopoutPanelManager
SetForegroundWindow(childWindow.Handle);
Thread.Sleep(500);
var image = TakeScreenShot(rect);
var image = TakeScreenShot(rect, childWindow.Handle.ToInt32());
MoveWindow(childWindow.Handle, rect.Left, rect.Top, originalWidth, originalHeight, true);
// OCR the image into text
@ -220,7 +219,7 @@ namespace MSFSPopoutPanelManager
return true;
}
private byte[] TakeScreenShot(Rect rect)
private byte[] TakeScreenShot(Rect rect, int imageId)
{
var bounds = new System.Drawing.Rectangle(rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top);
var bitmap = new Bitmap(bounds.Width, bounds.Height);
@ -237,9 +236,17 @@ namespace MSFSPopoutPanelManager
using(var img = SixLabors.ImageSharp.Image.Load(imageBytes))
{
img.Mutate(x => x.Invert().GaussianSharpen().Grayscale());
double imageRatio = 250 / 72.0; // change from default of 72 DPI to 250 DPI
img.Metadata.HorizontalResolution *= imageRatio;
img.Metadata.VerticalResolution *= imageRatio;
var memoryStream = new MemoryStream();
img.Save(memoryStream, new SixLabors.ImageSharp.Formats.Bmp.BmpEncoder());
img.Save(@".\test.jpg");
#if DEBUG
Directory.CreateDirectory("imageDebug");
img.Save(@$"./imageDebug/test-{imageId}.jpg");
#endif
return memoryStream.ToArray();
}
}

View file

@ -7,11 +7,18 @@ namespace MSFSPopoutPanelManager
{
public class FileManager
{
public static UserData ReadUserData()
private string _startupPath;
public FileManager(string startupPath)
{
_startupPath = startupPath;
}
public UserData ReadUserData()
{
try
{
using (StreamReader reader = new StreamReader(@".\config\userdata.json"))
using (StreamReader reader = new StreamReader(_startupPath + @"\config\userdata.json"))
{
string json = reader.ReadToEnd();
return JsonConvert.DeserializeObject<UserData>(json);
@ -23,18 +30,18 @@ namespace MSFSPopoutPanelManager
}
}
public static void WriteUserData(UserData userData)
public void WriteUserData(UserData userData)
{
using (StreamWriter file = File.CreateText(@".\config\userdata.json"))
using (StreamWriter file = File.CreateText(_startupPath + @"\config\userdata.json"))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(file, userData);
}
}
public static List<OcrEvalData> ReadProfileData()
public List<OcrEvalData> ReadProfileData()
{
using (StreamReader reader = new StreamReader(@".\config\ocrdata.json"))
using (StreamReader reader = new StreamReader(_startupPath + @"\config\ocrdata.json"))
{
string json = reader.ReadToEnd();

View file

@ -5,7 +5,7 @@
<TargetFramework>net5.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
<Platforms>x64</Platforms>
<Version>1.1</Version>
<Version>1.2</Version>
<AssemblyName>MSFSPopoutPanelManager</AssemblyName>
<RootNamespace>MSFSPopoutPanelManager</RootNamespace>
<ApplicationIcon>WindowManager.ico</ApplicationIcon>
@ -104,6 +104,9 @@
<None Update="LICENSE">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="VERSION.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Vesion.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>

View file

@ -12,7 +12,8 @@ namespace MSFSPopoutPanelManager
public partial class MainForm : Form
{
private SynchronizationContext _syncRoot;
private WindowManager _popoutWindowsManager;
private FileManager _fileManager;
private WindowManager _windowManager;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
@ -25,19 +26,22 @@ namespace MSFSPopoutPanelManager
public MainForm()
{
InitializeComponent();
_syncRoot = SynchronizationContext.Current;
_fileManager = new FileManager(Application.StartupPath);
_windowManager = new WindowManager(_fileManager);
_windowManager.OnStatusUpdated += HandleOnStatusUpdated;
_windowManager.OnSimulatorStarted += HandleOnSimulatorStarted;
_windowManager.OnOcrDebugged += HandleOnOcrDebugged;
_windowManager.CheckSimulatorStarted();
SetProfileDropDown();
_popoutWindowsManager = new WindowManager();
_popoutWindowsManager.OnStatusUpdated += HandleOnStatusUpdated;
_popoutWindowsManager.OnSimulatorStarted += HandleOnSimulatorStarted;
_popoutWindowsManager.OnOcrDebugged += HandleOnOcrDebugged;
_popoutWindowsManager.CheckSimulatorStarted();
#if DEBUG
// Set application windows always on top for easy debugging
SetWindowPos(this.Handle, HWND_TOPMOST, this.Left, this.Top, this.Width, this.Height, TOPMOST_FLAGS);
#endif
var version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
lblVersion.Text += version.ToString();
}
@ -47,7 +51,7 @@ namespace MSFSPopoutPanelManager
txtStatus.Clear();
var profile = GetProfileDropDown();
var success = _popoutWindowsManager.Analyze(profile);
var success = _windowManager.Analyze(profile);
btnApplySettings.Enabled = success;
btnSaveSettings.Enabled = success;
@ -58,7 +62,7 @@ namespace MSFSPopoutPanelManager
txtStatus.Clear();
var profile = GetProfileDropDown();
_popoutWindowsManager.ApplySettings(profile, chkHidePanelTitleBar.Checked, chkAlwaysOnTop.Checked);
_windowManager.ApplySettings(profile, chkHidePanelTitleBar.Checked, chkAlwaysOnTop.Checked);
}
private void btnSaveSettings_Click(object sender, EventArgs e)
@ -66,15 +70,15 @@ namespace MSFSPopoutPanelManager
txtStatus.Clear();
var profile = GetProfileDropDown();
_popoutWindowsManager.SaveSettings(profile, chkHidePanelTitleBar.Checked, chkAlwaysOnTop.Checked);
_windowManager.SaveSettings(profile, chkHidePanelTitleBar.Checked, chkAlwaysOnTop.Checked);
}
private void SetProfileDropDown()
{
try
{
var profileData = FileManager.ReadProfileData();
var profiles = profileData.Select(x => x.Profile).Distinct();
var profileData = _fileManager.ReadProfileData();
var profiles = profileData.Select(x => x.Profile).Distinct().OrderBy(x => x);
var defaultProfile = profileData.Find(x => x.DefaultProfile);
comboBoxProfile.DataSource = profiles.ToList();
@ -91,6 +95,32 @@ namespace MSFSPopoutPanelManager
return comboBoxProfile.SelectedItem.ToString();
}
private void comboBoxProfile_SelectedIndexChanged(object sender, EventArgs e)
{
_windowManager.Reset();
var userData = _fileManager.ReadUserData();
if (userData != null)
{
var userProfile = userData.Profiles.Find(x => x.Name == Convert.ToString(comboBoxProfile.SelectedValue));
if (userProfile != null)
{
chkAlwaysOnTop.Checked = userProfile.AlwaysOnTop;
chkHidePanelTitleBar.Checked = userProfile.HidePanelTitleBar;
}
else
{
// default values
chkAlwaysOnTop.Checked = false;
chkHidePanelTitleBar.Checked = false;
}
}
btnApplySettings.Enabled = false;
btnSaveSettings.Enabled = false;
}
private void HandleOnStatusUpdated(object source, EventArgs<string> arg)
{
_syncRoot.Post(SetStatusMessage, arg.Value);
@ -163,7 +193,7 @@ namespace MSFSPopoutPanelManager
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
// Put all panels popout back to original state
_popoutWindowsManager.RestorePanelTitleBar();
_windowManager.RestorePanelTitleBar();
}
private void MainForm_Resize(object sender, EventArgs e)
@ -186,28 +216,6 @@ namespace MSFSPopoutPanelManager
WindowState = FormWindowState.Normal;
}
private void comboBoxProfile_SelectedIndexChanged(object sender, EventArgs e)
{
var userData = FileManager.ReadUserData();
if (userData != null)
{
var userProfile = userData.Profiles.Find(x => x.Name == Convert.ToString(comboBoxProfile.SelectedValue));
if (userProfile != null)
{
chkAlwaysOnTop.Checked = userProfile.AlwaysOnTop;
chkHidePanelTitleBar.Checked = userProfile.HidePanelTitleBar;
}
else
{
// default values
chkAlwaysOnTop.Checked = false;
chkHidePanelTitleBar.Checked = false;
}
}
btnApplySettings.Enabled = false;
btnSaveSettings.Enabled = false;
}
}
}

View file

@ -1,13 +1,21 @@
# Version History
<hr/>
## Version 1.1
## Version 1.2.0.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.
* Fixed an issue of unable to set or reset panel to NOT ALWAYS ON TOP
* Fixed application path issue for not able to find ocrdata.json file at startup.
* Removed MSFS Pop Out Panel Manager is always on top (not the actual in-game panels themselves). Now it is only always on top during development debug mode if you compile and run the application from source code.
## Version 1.1.0.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.
* Added minimize application to tray feature.
* Made application flow more intuitive.
* Fix various small bugs in the application.
* Fixed various small bugs in the application.
## Version 1.0
## Version 1.0.0.0
* Initial Release

View file

@ -35,14 +35,20 @@ namespace MSFSPopoutPanelManager
const int WS_CAPTION = WS_BORDER | WS_DLGFRAME;
private const int MSFS_CONNECTION_RETRY_TIMEOUT = 2000;
private FileManager _fileManager;
private Timer _timer;
private UserData _userData;
private MainWindow _simWindow;
private AnalysisEngine _analysisEngine;
private Dictionary<IntPtr, Int64> _originalChildWindowStyles;
public WindowManager()
private bool _currentHidePanelTitleBarStatus;
private bool _currentAlwaysOnTopStatus;
public WindowManager(FileManager fileManager)
{
_fileManager = fileManager;
_analysisEngine = new AnalysisEngine();
_analysisEngine.OnStatusUpdated += (source, e) => OnStatusUpdated?.Invoke(source, e);
_analysisEngine.OnOcrDebugged += (source, e) => OnOcrDebugged?.Invoke(source, e);
@ -70,20 +76,33 @@ namespace MSFSPopoutPanelManager
};
}
public void Reset()
{
// reset these statuses
_currentHidePanelTitleBarStatus = false;
_currentAlwaysOnTopStatus = false;
RestorePanelTitleBar();
_originalChildWindowStyles = null;
}
public bool Analyze(string profileName)
{
_originalChildWindowStyles = null;
_simWindow.ChildWindowsData = new List<ChildWindow>();
_analysisEngine.Analyze(ref _simWindow, profileName);
var evalData = _fileManager.ReadProfileData().Find(x => x.Profile == profileName);
_analysisEngine.Analyze(ref _simWindow, evalData);
return _simWindow.ChildWindowsData.FindAll(x => x.PopoutType == PopoutType.Custom || x.PopoutType == PopoutType.BuiltIn).Count > 0;
}
public void ApplySettings(string profileName, bool showPanelTitleBar, bool alwaysOnTop)
public void ApplySettings(string profileName, bool hidePanelTitleBar, bool alwaysOnTop)
{
// Try to load previous profiles
_userData = FileManager.ReadUserData();
_userData = _fileManager.ReadUserData();
var profileSettings = _userData != null ? _userData.Profiles.Find(x => x.Name == profileName) : null;
if (profileSettings == null)
@ -98,8 +117,18 @@ namespace MSFSPopoutPanelManager
if (childWindows.Count > 0)
{
ApplyPositions(profileSettings, childWindows);
ApplyAlwaysOnTop(alwaysOnTop, childWindows);
ApplyHidePanelTitleBar(showPanelTitleBar, childWindows);
if (_currentHidePanelTitleBarStatus != hidePanelTitleBar)
{
_currentHidePanelTitleBarStatus = hidePanelTitleBar;
ApplyHidePanelTitleBar(hidePanelTitleBar, childWindows);
}
if(_currentAlwaysOnTopStatus != alwaysOnTop)
{
_currentAlwaysOnTopStatus = alwaysOnTop;
ApplyAlwaysOnTop(alwaysOnTop, childWindows);
}
}
}
@ -134,7 +163,7 @@ namespace MSFSPopoutPanelManager
}
}
FileManager.WriteUserData(_userData);
_fileManager.WriteUserData(_userData);
OnStatusUpdated?.Invoke(this, new EventArgs<string>("Pop out panel positions have been saved."));
}
}
@ -173,6 +202,15 @@ namespace MSFSPopoutPanelManager
SetWindowPos(childWindow.Handle, new IntPtr(-1), rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, SWP_ALWAYS_ON_TOP);
}
}
else
{
foreach (var childWindow in childWindows)
{
Rect rect = new Rect();
GetWindowRect(childWindow.Handle, ref rect);
SetWindowPos(childWindow.Handle, new IntPtr(-2), rect.Left, rect.Top, rect.Right - rect.Left, rect.Bottom - rect.Top, 0);
}
}
}
private void ApplyHidePanelTitleBar(bool hidePanelTitleBar, List<ChildWindow> childWindows)

View file

@ -1,7 +1,7 @@
[
// G1000
// G1000 NXi
{
"profile": "G1000",
"profile": "G1000 / G1000 NXi",
"defaultProfile": "true",
"ocrImageScale": 1.0,
"evalData": [
@ -35,9 +35,57 @@
}
]
},
// Flybywire A320NX
{
"profile": "Flybywire A320NX",
"defaultProfile": "false",
"ocrImageScale": 1,
"evalData": [
{
"popoutName": "Screen1",
"data": [ "QNH", "29.92" ]
},
{
"popoutName": "Screen2",
"data": [ "REL", "GW" ]
},
{
"popoutName": "Screen3",
"data": [ "TAS", "GS", "NM", "GPS", "Primary", "PPOS" ]
},
{
"popoutName": "Screen4",
"data": [ "F.FLOW", "AUTO BRK", "KG/MIN", "FOB", "TAT", "SAT" ]
}
]
},
// Asobo A320
{
"profile": "Asobo A320",
"defaultProfile": "false",
"ocrImageScale": 1,
"evalData": [
{
"popoutName": "Screen1",
"data": [ "IDLE" ]
},
{
"popoutName": "Screen2",
"data": [ "TAS", "GS", "NM" ]
},
{
"popoutName": "Screen3",
"data": [ "F.FLOW", "KG", "KG/MIN", "FOB", "TAT", "SAT" ]
},
{
"popoutName": "Screen4",
"data": [ "SPD", "SEL", "CLB", "NAV", "QNH" ]
}
]
},
// Custom Sample Profile - delete this if you don't need it
{
"profile": "Custom Sample Profile",
"profile": "ZZZ - Custom Sample Profile",
"defaultProfile": "false",
"ocrImageScale": 1.0,
"evalData": [