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

Version 3.3

This commit is contained in:
hawkeye 2022-04-17 23:38:33 -04:00
parent 973ebd3dd7
commit a90077b44c
33 changed files with 551 additions and 178 deletions

View file

@ -23,6 +23,7 @@ namespace MSFSPopoutPanelManager.FsConnector
SIMSTOP, SIMSTOP,
FLIGHTLOADED, FLIGHTLOADED,
VIEW, VIEW,
PAUSED PAUSED,
NONE
}; };
} }

View file

@ -5,13 +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.2.0</Version> <Version>3.3.0.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;AnyCPU</Platforms> <Platforms>x64;AnyCPU</Platforms>
<AssemblyVersion>3.3.0.0</AssemblyVersion>
<FileVersion>3.3.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -19,7 +19,7 @@ namespace MSFSPopoutPanelManager.FsConnector
public event EventHandler<EventArgs<dynamic>> OnReceivedData; public event EventHandler<EventArgs<dynamic>> OnReceivedData;
public event EventHandler OnConnected; public event EventHandler OnConnected;
public event EventHandler OnDisconnected; public event EventHandler OnDisconnected;
public event EventHandler<EventArgs<SimConnectSystemEvent>> OnReceiveSystemEvent; public event EventHandler<EventArgs<Tuple<SimConnectSystemEvent, uint>>> OnReceiveSystemEvent;
public dynamic SimData { get; set; } public dynamic SimData { get; set; }
@ -45,7 +45,7 @@ namespace MSFSPopoutPanelManager.FsConnector
_simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.SIMSTART, "SimStart"); _simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.SIMSTART, "SimStart");
_simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.SIMSTOP, "SimStop"); _simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.SIMSTOP, "SimStop");
_simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.VIEW, "View"); _simConnect.SubscribeToSystemEvent(SimConnectSystemEvent.VIEW, "View");
// Setup SimConnect data structure definition using SimConnectStruct and SimConnect data definitions // Setup SimConnect data structure definition using SimConnectStruct and SimConnect data definitions
var definitions = DataDefinition.GetDefinition(); var definitions = DataDefinition.GetDefinition();
foreach (var (PropName, SimConnectName, SimConnectUnit, SimConnectDataType, ObjectType) in definitions) foreach (var (PropName, SimConnectName, SimConnectUnit, SimConnectDataType, ObjectType) in definitions)
@ -183,12 +183,9 @@ namespace MSFSPopoutPanelManager.FsConnector
private void HandleOnReceiveEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data) private void HandleOnReceiveEvent(SimConnect sender, SIMCONNECT_RECV_EVENT data)
{ {
var systemEvent = ((SimConnectSystemEvent)data.uEventID); var systemEvent = ((SimConnectSystemEvent)data.uEventID);
var tuple = new Tuple<SimConnectSystemEvent, uint>(systemEvent, data.uEventID);
// Only look at VIEW for cockpit view during loading of flight (dwData = 2) OnReceiveSystemEvent?.Invoke(this, new EventArgs<Tuple<SimConnectSystemEvent, uint>>(tuple));
if (systemEvent == SimConnectSystemEvent.VIEW && data.dwData != 2)
return;
OnReceiveSystemEvent?.Invoke(this, new EventArgs<SimConnectSystemEvent>(systemEvent));
} }
} }
} }

View file

@ -24,10 +24,12 @@ namespace MSFSPopoutPanelManager.Model
MinimizeToTray = false; MinimizeToTray = false;
AlwaysOnTop = true; AlwaysOnTop = true;
UseAutoPanning = true; UseAutoPanning = true;
AutoPanningKeyBinding = "0";
StartMinimized = false; StartMinimized = false;
IncludeBuiltInPanel = false; IncludeBuiltInPanel = false;
AutoPopOutPanels = false; AutoPopOutPanels = false;
AutoPopOutPanelsWaitDelay = new AutoPopOutPanelsWaitDelay(); AutoPopOutPanelsWaitDelay = new AutoPopOutPanelsWaitDelay();
} }
public void Load() public void Load()
@ -36,10 +38,12 @@ namespace MSFSPopoutPanelManager.Model
this.MinimizeToTray = appSetting.MinimizeToTray; this.MinimizeToTray = appSetting.MinimizeToTray;
this.AlwaysOnTop = appSetting.AlwaysOnTop; this.AlwaysOnTop = appSetting.AlwaysOnTop;
this.UseAutoPanning = appSetting.UseAutoPanning; this.UseAutoPanning = appSetting.UseAutoPanning;
this.AutoPanningKeyBinding = appSetting.AutoPanningKeyBinding;
this.StartMinimized = appSetting.StartMinimized; this.StartMinimized = appSetting.StartMinimized;
this.IncludeBuiltInPanel = appSetting.IncludeBuiltInPanel; this.IncludeBuiltInPanel = appSetting.IncludeBuiltInPanel;
this.AutoPopOutPanels = appSetting.AutoPopOutPanels; this.AutoPopOutPanels = appSetting.AutoPopOutPanels;
this.AutoPopOutPanelsWaitDelay = appSetting.AutoPopOutPanelsWaitDelay; this.AutoPopOutPanelsWaitDelay = appSetting.AutoPopOutPanelsWaitDelay;
AutoPopOutPanelsWaitDelay.DataChanged += (e, source) => WriteAppSetting(this);
_saveEnabled = true; _saveEnabled = true;
} }
@ -61,19 +65,14 @@ namespace MSFSPopoutPanelManager.Model
} }
} }
//[OnDeserialized]
//private void OnDeserialized(StreamingContext context)
//{
// // Allow save data
// _saveEnabled = true;
//}
public bool MinimizeToTray { get; set; } public bool MinimizeToTray { get; set; }
public bool AlwaysOnTop { get; set; } public bool AlwaysOnTop { get; set; }
public bool UseAutoPanning { get; set; } public bool UseAutoPanning { get; set; }
public string AutoPanningKeyBinding { get; set; }
public bool StartMinimized { get; set; } public bool StartMinimized { get; set; }
public bool IncludeBuiltInPanel { get; set; } public bool IncludeBuiltInPanel { get; set; }
@ -130,18 +129,23 @@ namespace MSFSPopoutPanelManager.Model
serializer.Serialize(file, appSetting); serializer.Serialize(file, appSetting);
} }
} }
catch catch(Exception ex)
{ {
Logger.LogStatus($"Unable to write app setting data file: {APP_SETTING_DATA_FILENAME}", StatusMessageType.Error); Logger.LogStatus($"Unable to write app setting data file: {APP_SETTING_DATA_FILENAME}", StatusMessageType.Error);
} }
} }
} }
public class AutoPopOutPanelsWaitDelay public class AutoPopOutPanelsWaitDelay : INotifyPropertyChanged
{ {
// Using PropertyChanged.Fody
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler DataChanged;
public AutoPopOutPanelsWaitDelay() public AutoPopOutPanelsWaitDelay()
{ {
ReadyToFlyButton = 4; ReadyToFlyButton = 6;
InitialCockpitView = 2; InitialCockpitView = 2;
InstrumentationPowerOn = 2; InstrumentationPowerOn = 2;
} }
@ -151,5 +155,10 @@ namespace MSFSPopoutPanelManager.Model
public int InitialCockpitView { get; set; } public int InitialCockpitView { get; set; }
public int InstrumentationPowerOn { get; set; } public int InstrumentationPowerOn { get; set; }
public void OnPropertyChanged(string propertyName, object before, object after)
{
DataChanged?.Invoke(this, null);
}
} }
} }

View file

@ -5,7 +5,7 @@
<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.2.0</Version> <Version>3.3.0.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>

View file

@ -1,4 +1,6 @@
using Newtonsoft.Json; using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.ComponentModel; using System.ComponentModel;
@ -12,6 +14,7 @@ namespace MSFSPopoutPanelManager.Model
{ {
PanelSourceCoordinates = new ObservableCollection<PanelSourceCoordinate>(); PanelSourceCoordinates = new ObservableCollection<PanelSourceCoordinate>();
PanelConfigs = new ObservableCollection<PanelConfig>(); PanelConfigs = new ObservableCollection<PanelConfig>();
BindingPlaneTitle = new ObservableCollection<string>();
IsLocked = false; IsLocked = false;
} }
@ -21,7 +24,8 @@ namespace MSFSPopoutPanelManager.Model
public bool IsDefaultProfile { get; set; } public bool IsDefaultProfile { get; set; }
public string BindingPlaneTitle { get; set; } [JsonConverter(typeof(SingleValueArrayConvertor<string>))]
public ObservableCollection<string> BindingPlaneTitle { get; set; }
public bool IsLocked { get; set; } public bool IsLocked { get; set; }
@ -44,7 +48,41 @@ namespace MSFSPopoutPanelManager.Model
[JsonIgnore] [JsonIgnore]
public bool HasBindingPlaneTitle public bool HasBindingPlaneTitle
{ {
get { return !string.IsNullOrEmpty(BindingPlaneTitle); } get { return BindingPlaneTitle.Count > 0; }
} }
} }
public class SingleValueArrayConvertor<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object val = new object();
if(reader.TokenType == JsonToken.String)
{
var instance = (string)serializer.Deserialize(reader, typeof(string));
val = new ObservableCollection<string>() { instance };
}
else if(reader.TokenType == JsonToken.StartArray)
{
val = serializer.Deserialize(reader, objectType);
}
else if(reader.TokenType == JsonToken.Null)
{
val = new ObservableCollection<string>();
}
return val;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
} }

View file

@ -60,8 +60,10 @@ namespace MSFSPopoutPanelManager.Provider
Thread.Sleep(200); Thread.Sleep(200);
} }
public static void SaveCustomViewZero(IntPtr hwnd) public static void SaveCustomView(IntPtr hwnd, string keybinding)
{ {
uint customViewKey = (uint) (Convert.ToInt32(keybinding) + KEY_0);
PInvoke.SetForegroundWindow(hwnd); PInvoke.SetForegroundWindow(hwnd);
Thread.Sleep(500); Thread.Sleep(500);
@ -71,15 +73,17 @@ namespace MSFSPopoutPanelManager.Provider
// Set view using Ctrl-Alt-0 // Set view using Ctrl-Alt-0
PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYDOWN, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0);
PInvoke.keybd_event(Convert.ToByte(KEY_0), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYDOWN, 0);
Thread.Sleep(200); Thread.Sleep(200);
PInvoke.keybd_event(Convert.ToByte(KEY_0), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYUP, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 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); PInvoke.keybd_event(Convert.ToByte(VK_LCONTROL), 0, KEYEVENTF_KEYUP, 0);
} }
public static void LoadCustomViewZero(IntPtr hwnd) public static void LoadCustomView(IntPtr hwnd, string keybinding)
{ {
uint customViewKey = (uint)(Convert.ToInt32(keybinding) + KEY_0);
PInvoke.SetForegroundWindow(hwnd); PInvoke.SetForegroundWindow(hwnd);
Thread.Sleep(500); Thread.Sleep(500);
@ -96,9 +100,9 @@ namespace MSFSPopoutPanelManager.Provider
// Then load view using Alt-0 // Then load view using Alt-0
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYDOWN, 0);
PInvoke.keybd_event(Convert.ToByte(KEY_0), 0, KEYEVENTF_KEYDOWN, 0); PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYDOWN, 0);
Thread.Sleep(200); Thread.Sleep(200);
PInvoke.keybd_event(Convert.ToByte(KEY_0), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(customViewKey), 0, KEYEVENTF_KEYUP, 0);
PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYUP, 0); PInvoke.keybd_event(Convert.ToByte(VK_LMENU), 0, KEYEVENTF_KEYUP, 0);
} }

View file

@ -21,13 +21,20 @@ namespace MSFSPopoutPanelManager.Provider
public const int SWP_NOMOVE = 0x0002; public const int SWP_NOMOVE = 0x0002;
public const int SWP_NOSIZE = 0x0001; public const int SWP_NOSIZE = 0x0001;
public const int SWP_FRAMECHANGED = 0x0020;
public const int SWP_NOREDRAW = 0x0008;
public const int SWP_ASYNCWINDOWPOS = 0x4000;
public const int SWP_ALWAYS_ON_TOP = SWP_NOMOVE | SWP_NOSIZE; public const int SWP_ALWAYS_ON_TOP = SWP_NOMOVE | SWP_NOSIZE;
public const int GWL_STYLE = -16; public const int GWL_STYLE = -16;
public const int WS_SIZEBOX = 0x00040000; public const int GWL_EXSTYLE = -20;
public const int WS_BORDER = 0x00800000; public const uint WS_SIZEBOX = 0x00040000;
public const int WS_DLGFRAME = 0x00400000; public const uint WS_BORDER = 0x00800000;
public const int WS_CAPTION = WS_BORDER | WS_DLGFRAME; public const uint WS_DLGFRAME = 0x00400000;
public const uint WS_CAPTION = WS_BORDER | WS_DLGFRAME;
public const uint WS_POPUP = 0x80000000;
public const uint WS_EX_DLGMODALFRAME = 0x00000001;
public const uint WS_THICKFRAME = 0x00040000;
public const int HWND_TOPMOST = -1; public const int HWND_TOPMOST = -1;
public const int HWND_NOTOPMOST = -2; public const int HWND_NOTOPMOST = -2;

View file

@ -61,21 +61,20 @@ namespace MSFSPopoutPanelManager.Provider
break; break;
case PanelConfigPropertyName.Left: case PanelConfigPropertyName.Left:
case PanelConfigPropertyName.Top: case PanelConfigPropertyName.Top:
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar)
return;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true); PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true);
break; break;
case PanelConfigPropertyName.Width: case PanelConfigPropertyName.Width:
case PanelConfigPropertyName.Height: case PanelConfigPropertyName.Height:
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar) if (panelConfig.HideTitlebar)
return; WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
int orignalLeft = panelConfig.Left; int orignalLeft = panelConfig.Left;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true); PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, true);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height); MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height);
if (panelConfig.HideTitlebar)
WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
break; break;
case PanelConfigPropertyName.AlwaysOnTop: case PanelConfigPropertyName.AlwaysOnTop:
WindowManager.ApplyAlwaysOnTop(panelConfig.PanelHandle, panelConfig.AlwaysOnTop, new Rectangle(panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height)); WindowManager.ApplyAlwaysOnTop(panelConfig.PanelHandle, panelConfig.AlwaysOnTop, new Rectangle(panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height));
@ -99,32 +98,43 @@ namespace MSFSPopoutPanelManager.Provider
if (index > -1) if (index > -1)
{ {
var panelConfig = UserProfile.PanelConfigs[index]; var panelConfig = UserProfile.PanelConfigs[index];
// Do not allow changes to panel if title bar is hidden. This will cause the panel to resize incorrectly
if (panelConfig.HideTitlebar)
return;
int orignalLeft = panelConfig.Left; int orignalLeft = panelConfig.Left;
switch (panelConfigItem.PanelConfigProperty) switch (panelConfigItem.PanelConfigProperty)
{ {
case PanelConfigPropertyName.Left: case PanelConfigPropertyName.Left:
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left + changeAmount, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
panelConfig.Left += changeAmount; panelConfig.Left += changeAmount;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
break; break;
case PanelConfigPropertyName.Top: case PanelConfigPropertyName.Top:
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top + changeAmount, panelConfig.Width, panelConfig.Height, false);
panelConfig.Top += changeAmount; panelConfig.Top += changeAmount;
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
break; break;
case PanelConfigPropertyName.Width: case PanelConfigPropertyName.Width:
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width + changeAmount, panelConfig.Height, false);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width + changeAmount, panelConfig.Height);
panelConfig.Width += changeAmount; panelConfig.Width += changeAmount;
if (panelConfig.HideTitlebar)
WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height);
if (panelConfig.HideTitlebar)
WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
break; break;
case PanelConfigPropertyName.Height: case PanelConfigPropertyName.Height:
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height + changeAmount, false);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height + changeAmount);
panelConfig.Height += changeAmount; panelConfig.Height += changeAmount;
if (panelConfig.HideTitlebar)
WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, false);
PInvoke.MoveWindow(panelConfig.PanelHandle, panelConfig.Left, panelConfig.Top, panelConfig.Width, panelConfig.Height, false);
MSFSBugPanelShiftWorkaround(panelConfig.PanelHandle, orignalLeft, panelConfig.Top, panelConfig.Width, panelConfig.Height);
if (panelConfig.HideTitlebar)
WindowManager.ApplyHidePanelTitleBar(panelConfig.PanelHandle, true);
break; break;
default: default:
return; return;
@ -211,9 +221,18 @@ namespace MSFSPopoutPanelManager.Provider
panelConfig.Left = winRectangle.Left; panelConfig.Left = winRectangle.Left;
panelConfig.Top = winRectangle.Top; panelConfig.Top = winRectangle.Top;
panelConfig.Width = clientRectangle.Width + 16;
panelConfig.Height = clientRectangle.Height + 39; if (panelConfig.HideTitlebar)
{
panelConfig.Width = clientRectangle.Width;
panelConfig.Height = clientRectangle.Height;
}
else
{
panelConfig.Width = clientRectangle.Width + 16;
panelConfig.Height = clientRectangle.Height + 39;
}
// Detect if window is maximized, if so, save settings // Detect if window is maximized, if so, save settings
WINDOWPLACEMENT wp = new WINDOWPLACEMENT(); WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp); wp.length = System.Runtime.InteropServices.Marshal.SizeOf(wp);

View file

@ -52,7 +52,7 @@ namespace MSFSPopoutPanelManager.Provider
var simualatorProcess = DiagnosticManager.GetSimulatorProcess(); var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null) if (simualatorProcess != null)
{ {
InputEmulationManager.LoadCustomViewZero(simualatorProcess.Handle); InputEmulationManager.LoadCustomView(simualatorProcess.Handle, AppSetting.AutoPanningKeyBinding);
Thread.Sleep(500); Thread.Sleep(500);
} }
} }

View file

@ -86,7 +86,7 @@ namespace MSFSPopoutPanelManager.Provider
var simualatorProcess = DiagnosticManager.GetSimulatorProcess(); var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null) if (simualatorProcess != null)
{ {
InputEmulationManager.SaveCustomViewZero(simualatorProcess.Handle); InputEmulationManager.SaveCustomView(simualatorProcess.Handle, AppSetting.AutoPanningKeyBinding);
} }
} }

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.2.0</Version> <Version>3.3.0.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

@ -17,6 +17,7 @@ namespace MSFSPopoutPanelManager.Provider
private System.Timers.Timer _requestDataTimer; private System.Timers.Timer _requestDataTimer;
private SimConnectSystemEvent _lastSystemEvent; private SimConnectSystemEvent _lastSystemEvent;
private bool _isSimActive;
private bool _isPowerOnForPopOut; private bool _isPowerOnForPopOut;
public event EventHandler OnConnected; public event EventHandler OnConnected;
@ -43,16 +44,19 @@ namespace MSFSPopoutPanelManager.Provider
_requestDataTimer.Elapsed += HandleMessageReceived; _requestDataTimer.Elapsed += HandleMessageReceived;
}; };
_isSimActive = false;
_simConnector.Start(); _simConnector.Start();
} }
public void Stop() public void Stop()
{ {
_isSimActive = false;
_simConnector.Stop(); _simConnector.Stop();
} }
public void Restart() public void Restart()
{ {
_isSimActive = false;
_simConnector.StopAndReconnect(); _simConnector.StopAndReconnect();
} }
@ -115,17 +119,32 @@ namespace MSFSPopoutPanelManager.Provider
OnSimConnectDataRefreshed?.Invoke(this, new EventArgs<dynamic>(e.Value)); OnSimConnectDataRefreshed?.Invoke(this, new EventArgs<dynamic>(e.Value));
} }
private void HandleReceiveSystemEvent(object sender, EventArgs<SimConnectSystemEvent> e) private void HandleReceiveSystemEvent(object sender, EventArgs<Tuple<SimConnectSystemEvent, uint>> e)
{ {
var systemEvent = e.Value.Item1;
var dwData = e.Value.Item2;
Debug.WriteLine($"SimConnectSystemEvent Received: {systemEvent} dwData: {dwData}");
// to detect flight start at the "Ready to Fly" screen, it has a SIMSTART follows by a VIEW event // to detect flight start at the "Ready to Fly" screen, it has a SIMSTART follows by a VIEW event
if(_lastSystemEvent == SimConnectSystemEvent.SIMSTART && e.Value == SimConnectSystemEvent.VIEW) if (_lastSystemEvent == SimConnectSystemEvent.SIMSTART && systemEvent == SimConnectSystemEvent.VIEW && dwData == 4)
{
_isSimActive = true;
OnFlightStarted?.Invoke(this, null); OnFlightStarted?.Invoke(this, null);
return;
if (e.Value == SimConnectSystemEvent.SIMSTOP) }
// look for pair of events denoting sim ended after sim is active
if ((_isSimActive && _lastSystemEvent == SimConnectSystemEvent.SIMSTOP && systemEvent == SimConnectSystemEvent.VIEW && dwData == 4) ||
(_isSimActive && _lastSystemEvent == SimConnectSystemEvent.SIMSTOP && systemEvent == SimConnectSystemEvent.SIMSTART && dwData == 1))
{
_isSimActive = false;
_lastSystemEvent = SimConnectSystemEvent.NONE;
OnFlightStopped?.Invoke(this, null); OnFlightStopped?.Invoke(this, null);
return;
}
Debug.WriteLine($"SimConnectSystemEvent Received: {e.Value.ToString()}"); _lastSystemEvent = systemEvent;
_lastSystemEvent = e.Value;
} }
} }
} }

View file

@ -30,7 +30,7 @@ namespace MSFSPopoutPanelManager.Provider
var copiedProfile = matchedProfile.Copy<UserProfile>(); // Using Shared/ObjectExtensions.cs extension method var copiedProfile = matchedProfile.Copy<UserProfile>(); // Using Shared/ObjectExtensions.cs extension method
copiedProfile.IsDefaultProfile = false; copiedProfile.IsDefaultProfile = false;
copiedProfile.BindingPlaneTitle = null; copiedProfile.BindingPlaneTitle = new ObservableCollection<string>();
return AddProfile(copiedProfile, newProfileName); return AddProfile(copiedProfile, newProfileName);
} }
@ -83,21 +83,22 @@ namespace MSFSPopoutPanelManager.Provider
public void AddProfileBinding(string planeTitle, int activeProfileId) public void AddProfileBinding(string planeTitle, int activeProfileId)
{ {
var bindedProfile = UserProfiles.FirstOrDefault(p => p.BindingPlaneTitle == planeTitle); var bindedProfile = UserProfiles.FirstOrDefault(p => p.BindingPlaneTitle.ToList().Exists(p => p == planeTitle));
if (bindedProfile != null) if (bindedProfile != null)
{ {
Logger.LogStatus($"Unable to add binding to the profile because '{planeTitle}' was already bound to profile '{bindedProfile.ProfileName}'.", StatusMessageType.Error); Logger.LogStatus($"Unable to add binding to the profile because '{planeTitle}' was already bound to profile '{bindedProfile.ProfileName}'.", StatusMessageType.Error);
return; return;
} }
UserProfiles.First(p => p.ProfileId == activeProfileId).BindingPlaneTitle = planeTitle; UserProfiles.First(p => p.ProfileId == activeProfileId).BindingPlaneTitle.Add(planeTitle);
WriteUserProfiles(); WriteUserProfiles();
Logger.LogStatus($"Binding for the profile has been added successfully.", StatusMessageType.Info); Logger.LogStatus($"Binding for the profile has been added successfully.", StatusMessageType.Info);
} }
public void DeleteProfileBinding(int activeProfileId) public void DeleteProfileBinding(string planeTitle, int activeProfileId)
{ {
UserProfiles.First(p => p.ProfileId == activeProfileId).BindingPlaneTitle = null; UserProfiles.First(p => p.ProfileId == activeProfileId).BindingPlaneTitle.Remove(planeTitle);
WriteUserProfiles(); WriteUserProfiles();
Logger.LogStatus($"Binding for the profile has been deleted successfully.", StatusMessageType.Info); Logger.LogStatus($"Binding for the profile has been deleted successfully.", StatusMessageType.Info);
} }
@ -111,7 +112,7 @@ namespace MSFSPopoutPanelManager.Provider
UserProfiles = new ObservableCollection<UserProfile>(JsonConvert.DeserializeObject<List<UserProfile>>(reader.ReadToEnd())); UserProfiles = new ObservableCollection<UserProfile>(JsonConvert.DeserializeObject<List<UserProfile>>(reader.ReadToEnd()));
} }
} }
catch catch(Exception ex)
{ {
UserProfiles = new ObservableCollection<UserProfile>(new List<UserProfile>()); UserProfiles = new ObservableCollection<UserProfile>(new List<UserProfile>());
} }

View file

@ -1,5 +1,4 @@
using MSFSPopoutPanelManager.Shared; using System;
using System;
using System.Drawing; using System.Drawing;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@ -45,7 +44,7 @@ namespace MSFSPopoutPanelManager.Provider
{ {
Rectangle rectangle; Rectangle rectangle;
PInvoke.GetClientRect(handle, out rectangle); PInvoke.GetClientRect(handle, out rectangle);
PInvoke.MoveWindow(handle, x, y, rectangle.Width, rectangle.Height, false); PInvoke.MoveWindow(handle, x, y, rectangle.Width, rectangle.Height, true);
} }
public static void MinimizeWindow(IntPtr handle) public static void MinimizeWindow(IntPtr handle)

View file

@ -153,6 +153,8 @@ The user plane profile data and application settings data are stored as JSON fil
* Unable to pop out ALL panels. This may indicate a potential miscount of selected panels (circles) and the number of actual panels that got popped out. You may have duplicate panels in your selection or panels that cannot be popped out. * Unable to pop out ALL panels. This may indicate a potential miscount of selected panels (circles) and the number of actual panels that got popped out. You may have duplicate panels in your selection or panels that cannot be popped out.
* If you encounter application crashes or unknown error, please help my continuing development effort by attaching the file **error.log** in the application folder and open an issue ticket in github repo for this project. This is going to help me troubleshoot the issue and provide hotfixes. * If you encounter application crashes or unknown error, please help my continuing development effort by attaching the file **error.log** in the application folder and open an issue ticket in github repo for this project. This is going to help me troubleshoot the issue and provide hotfixes.
* If you encounter an issue with panels that are not restored back to your saved profile locations, please check if you have other apps such as Sizer or Windows PowerToys that may have conflict with Pop Out Manager.
## Author ## Author

View file

@ -9,7 +9,7 @@
<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.2.0</Version> <Version>3.3.0.0</Version>
<Platforms>AnyCPU;x64</Platforms> <Platforms>AnyCPU;x64</Platforms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
</PropertyGroup> </PropertyGroup>

View file

@ -2,7 +2,7 @@
<hr/> <hr/>
## Version 3.2.0 (Release) ## Version 3.2.0.1 (v3.2 Release)
* Added application auto update support. By installing this version of the app, auto update functionality will be available for all future versions of the application. * Added application auto update support. By installing this version of the app, auto update functionality will be available for all future versions of the application.
* Disabled panel adjustments when Hide Title Bar is checked for a panel. This is to fix an issue where panel adjustments (X-Pos, Y-Pos, Width, and Height) will behave erratically when Hide Title Bar is checked. * Disabled panel adjustments when Hide Title Bar is checked for a panel. This is to fix an issue where panel adjustments (X-Pos, Y-Pos, Width, and Height) will behave erratically when Hide Title Bar is checked.
* Increased default delay for auto-clicking "Ready to Fly" button from 2 seconds to 4 seconds in regard to Auto Pop Out Panels feature . [Fixed Issue #9](https://github.com/hawkeye-stan/msfs-popout-panel-manager/issues/9) * Increased default delay for auto-clicking "Ready to Fly" button from 2 seconds to 4 seconds in regard to Auto Pop Out Panels feature . [Fixed Issue #9](https://github.com/hawkeye-stan/msfs-popout-panel-manager/issues/9)

View file

@ -2,6 +2,7 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls" xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.WpfApp" xmlns:local="clr-namespace:MSFSPopoutPanelManager.WpfApp"
StartupUri="ApplicationWindow.xaml"> StartupUri="ApplicationWindow.xaml">
<Application.Resources> <Application.Resources>

View file

@ -124,7 +124,7 @@ namespace MSFSPopoutPanelManager.WpfApp
var process = DiagnosticManager.GetApplicationProcess(); var process = DiagnosticManager.GetApplicationProcess();
PROCESS_DPI_AWARENESS outValue; PROCESS_DPI_AWARENESS outValue;
GetProcessDpiAwareness(process.Handle, out outValue); GetProcessDpiAwareness(process.Handle, out outValue);
log4net.Info($"DPI Awareness is set to: {outValue}"); //log4net.Info($"DPI Awareness is set to: {outValue}");
} }
[DllImport("User32.dll")] [DllImport("User32.dll")]

View file

@ -53,15 +53,7 @@
<MenuItem Header="File"> <MenuItem Header="File">
<MenuItem Name="menuItem_Restart" Header="Restart" Command="{Binding Path=RestartCommand}" IsEnabled="{Binding Path=IsShownPanelConfigurationScreen}"/> <MenuItem Name="menuItem_Restart" Header="Restart" Command="{Binding Path=RestartCommand}" IsEnabled="{Binding Path=IsShownPanelConfigurationScreen}"/>
<Separator /> <Separator />
<MenuItem Header="Preferences"> <MenuItem Header="Preferences" Click="EditPreferences_Click" />
<MenuItem Name="menuItem_AlwaysOnTop" Header="Always on Top" IsChecked="{Binding Path=DataStore.AppSetting.AlwaysOnTop, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_AutoPanning" Header="Auto Panning" IsChecked="{Binding Path=DataStore.AppSetting.UseAutoPanning, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_AutoPopOutPanels" Header="Auto Pop Out Panels" IsChecked="{Binding Path=DataStore.AppSetting.AutoPopOutPanels, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_AutoStart" Header="Auto Start" IsChecked="{Binding Path=DataStore.AppSetting.AutoStart, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_IncludeBuiltInPanel" Header="Include Built-in Panels" IsChecked="{Binding Path=DataStore.AppSetting.IncludeBuiltInPanel, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_MinimizeToTray" Header="Minimize to Tray" IsChecked="{Binding Path=DataStore.AppSetting.MinimizeToTray, Mode=TwoWay}" IsCheckable="True"/>
<MenuItem Name="menuItem_StartMinimized" Header="Start Minimized" IsChecked="{Binding Path=DataStore.AppSetting.StartMinimized, Mode=TwoWay}" IsCheckable="True"/>
</MenuItem>
<Separator /> <Separator />
<MenuItem Name="menuItem_Exit" Header="Exit" Command="{Binding Path=ExitCommand}"/> <MenuItem Name="menuItem_Exit" Header="Exit" Command="{Binding Path=ExitCommand}"/>
</MenuItem> </MenuItem>

View file

@ -99,5 +99,15 @@ namespace MSFSPopoutPanelManager.WpfApp
{ {
_viewModel.ExitCommand.Execute(null); _viewModel.ExitCommand.Execute(null);
} }
private void EditPreferences_Click(object sender, RoutedEventArgs e)
{
PreferencesDialog dialog = new PreferencesDialog(_viewModel.DataStore.AppSetting);
dialog.Owner = Application.Current.MainWindow;
dialog.Topmost = true;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
dialog.ShowDialog();
}
} }
} }

View file

@ -31,22 +31,32 @@ namespace MSFSPopoutPanelManager.WpfApp
this.Loaded += (sender, e) => this.Loaded += (sender, e) =>
{ {
var dialogHandle = new WindowInteropHelper(Window.GetWindow(this)).Handle; var winObj = Window.GetWindow(this);
var simulatorProcessHandle = DiagnosticManager.GetSimulatorProcess().Handle;
Rectangle rectangle; if (winObj != null)
PInvoke.GetWindowRect(DiagnosticManager.GetSimulatorProcess().Handle, out rectangle); {
Rectangle clientRectangle; var window = new WindowInteropHelper(winObj);
PInvoke.GetClientRect(DiagnosticManager.GetSimulatorProcess().Handle, out clientRectangle);
var x = Convert.ToInt32(rectangle.X + clientRectangle.Width / 2 - this.Width / 2); if (window != null)
var y = Convert.ToInt32(rectangle.Y + clientRectangle.Height / 2 - this.Height / 2); {
var dialogHandle = window.Handle;
var simulatorProcessHandle = DiagnosticManager.GetSimulatorProcess().Handle;
Debug.WriteLine($"Game Location: X:{rectangle.X} Y:{rectangle.Y}"); Rectangle rectangle;
Debug.WriteLine($"Game Rectangle: Width:{clientRectangle.Width} Height:{clientRectangle.Height}"); PInvoke.GetWindowRect(DiagnosticManager.GetSimulatorProcess().Handle, out rectangle);
Debug.WriteLine($"Message Dialog Location: X:{x} Y:{y}"); Rectangle clientRectangle;
PInvoke.GetClientRect(DiagnosticManager.GetSimulatorProcess().Handle, out clientRectangle);
PInvoke.MoveWindow(dialogHandle, x, y, Convert.ToInt32(this.Width), Convert.ToInt32(this.Height), false); var x = Convert.ToInt32(rectangle.X + clientRectangle.Width / 2 - this.Width / 2);
var y = Convert.ToInt32(rectangle.Y + clientRectangle.Height / 2 - this.Height / 2);
Debug.WriteLine($"Game Location: X:{rectangle.X} Y:{rectangle.Y}");
Debug.WriteLine($"Game Rectangle: Width:{clientRectangle.Width} Height:{clientRectangle.Height}");
Debug.WriteLine($"Message Dialog Location: X:{x} Y:{y}");
PInvoke.MoveWindow(dialogHandle, x, y, Convert.ToInt32(this.Width), Convert.ToInt32(this.Height), false);
}
}
}; };
System.Timers.Timer timer = new System.Timers.Timer(); System.Timers.Timer timer = new System.Timers.Timer();

View file

@ -0,0 +1,101 @@
<mah:MetroWindow x:Class="MSFSPopoutPanelManager.WpfApp.PreferencesDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
xmlns:c="clr-namespace:CalcBinding;assembly=CalcBinding"
xmlns:local="clr-namespace:MSFSPopoutPanelManager.WpfApp"
mc:Ignorable="d"
Title="Preferences"
Height="450"
Width="800"
ResizeMode="NoResize"
Background="Transparent">
<Grid>
<DockPanel>
<TreeView Width="250" VerticalAlignment="Stretch" DockPanel.Dock="Left">
<TreeView.ItemContainerStyle>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="IsExpanded" Value="True" />
<Setter Property="Foreground" Value="#666666" />
<Setter Property="Background" Value="Transparent" />
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="White"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey }" Color="Transparent"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey }" Color="White"/>
</Style.Resources>
</Style>
</TreeView.ItemContainerStyle>
<TreeViewItem Header="Application Settings" Selected="TreeViewItem_Selected" Margin="0,15,0,10" IsSelected="True"></TreeViewItem>
<TreeViewItem Header="Pop Out Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
<TreeViewItem Header="Auto Pop Out Settings" Selected="TreeViewItem_Selected" Margin="0,0,0,10"></TreeViewItem>
</TreeView>
<WrapPanel Orientation="Vertical" DockPanel.Dock="Right" Margin="20,5,0,0" >
<WrapPanel Orientation="Vertical" Margin="0,10,0,0" Width="500" Visibility="{Binding Path=ApplicationSettingsVisibility, Mode=TwoWay}">
<CheckBox Margin="0,0,0,0" IsChecked="{Binding Path=AppSetting.AlwaysOnTop, Mode=TwoWay}" Content="Always on Top"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Pin the application on top of all open windows.</TextBlock>
<CheckBox Margin="0,0,0,0" IsChecked="{Binding Path=AppSetting.AutoStart, Mode=TwoWay}" Content="Auto Start"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Auto start the application when MSFS starts. This adds a XML config entry in EXE.xml file.</TextBlock>
<CheckBox IsChecked="{Binding Path=AppSetting.MinimizeToTray, Mode=TwoWay}" Content="Minimize to Tray"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Minimize the application to system tray.</TextBlock>
<CheckBox IsChecked="{Binding Path=AppSetting.StartMinimized, Mode=TwoWay}" Content="Start Minimized"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Start the application in minimized mode in system tray.</TextBlock>
</WrapPanel>
<WrapPanel Orientation="Vertical" Margin="0,10,0,0" Visibility="{Binding Path=PopOutSettingsVisibility, Mode=TwoWay}">
<CheckBox IsChecked="{Binding Path=AppSetting.UseAutoPanning, Mode=TwoWay}" Content="Auto Panning"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Enable automatic panning of cockpit view when popping out panels. Auto Panning remembers the custom cockpit camera angle you used when defining the locations of pop out panel.</TextBlock>
<TextBlock Margin="24,10,0,0" Width="Auto" TextWrapping="Wrap" >Key Binding</TextBlock>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Configure key binding for saving and recalling of custom MSFS cockpit camera view when defining the locations of pop out panel. Default is Ctrl-Alt-0.</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="20,10,0,0">
<Label Content="Ctrl-Alt-" FontSize="14"></Label>
<ComboBox HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="60"
SelectedValuePath="Tag"
SelectedValue="{Binding Path=AppSetting.AutoPanningKeyBinding, Mode=TwoWay}" >
<ComboBoxItem Content="0" Tag="0"></ComboBoxItem>
<ComboBoxItem Content="1" Tag="1"></ComboBoxItem>
<ComboBoxItem Content="2" Tag="2"></ComboBoxItem>
<ComboBoxItem Content="3" Tag="3"></ComboBoxItem>
<ComboBoxItem Content="4" Tag="4"></ComboBoxItem>
<ComboBoxItem Content="5" Tag="5"></ComboBoxItem>
<ComboBoxItem Content="6" Tag="6"></ComboBoxItem>
<ComboBoxItem Content="7" Tag="7"></ComboBoxItem>
<ComboBoxItem Content="8" Tag="8"></ComboBoxItem>
<ComboBoxItem Content="9" Tag="9"></ComboBoxItem>
</ComboBox>
</WrapPanel>
<CheckBox Margin="0,15,0,0" IsChecked="{Binding Path=AppSetting.IncludeBuiltInPanel, Mode=TwoWay}" Content="Include Built-in Panels"></CheckBox>
<TextBlock Margin="24,5,0,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Enable saving and recalling of MSFS built-in panels (ie. ATC, VFR Map, Checklist, etc) as part of profile definition.</TextBlock>
</WrapPanel>
<WrapPanel Orientation="Vertical" Margin="0,10,0,0" Visibility="{Binding Path=AutoPopOutSettingsVisibility, Mode=TwoWay}">
<CheckBox IsChecked="{Binding Path=AppSetting.AutoPopOutPanels, Mode=TwoWay}" Content="Auto Pop Out Panels"></CheckBox>
<TextBlock Margin="24,10,0,10" Width="Auto" TextWrapping="Wrap" >Wait delay for each step during auto pop out process (in seconds)</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="24,0,0,0" >
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Height="32" Value="{Binding Path=AppSetting.AutoPopOutPanelsWaitDelay.ReadyToFlyButton, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0">Ready to Fly</Label>
</WrapPanel>
<TextBlock Margin="119,0,10,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Amount of time to wait for 'Ready to Fly' button to appear before simulating mouse click.<LineBreak/>(Default: 6 seconds)</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="24,0,0,0" >
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Height="32" Value="{Binding Path=AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0" >Initial Cockpit View</Label>
</WrapPanel>
<TextBlock Margin="119,0,10,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Amount of time to wait for the cockpit to appear before beginning pop out panel process.<LineBreak/>(Default: 2 seconds)</TextBlock>
<WrapPanel Orientation="Horizontal" Margin="24,0,0,00" >
<mah:NumericUpDown Width="80" Minimum="1" Maximum="30" FontSize="16" Height="32" Value="{Binding Path=AppSetting.AutoPopOutPanelsWaitDelay.InstrumentationPowerOn, Mode=TwoWay}"></mah:NumericUpDown>
<Label Margin="10,0,0,0">Instrumentation Power On</Label>
</WrapPanel>
<TextBlock Margin="119,0,10,10" Width="Auto" TextWrapping="Wrap" FontSize="14">Amount of time to wait for cold start instrumentation power on to complete before beginning pop out panel process.<LineBreak/>(Default: 2 seconds)</TextBlock>
<WrapPanel Orientation="Horizontal"></WrapPanel>
</WrapPanel>
<WrapPanel Orientation="Vertical" Visibility="Visible">
</WrapPanel>
</WrapPanel>
</DockPanel>
</Grid>
</mah:MetroWindow>

View file

@ -0,0 +1,63 @@
using MahApps.Metro.Controls;
using MSFSPopoutPanelManager.Model;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace MSFSPopoutPanelManager.WpfApp
{
/// <summary>
/// Interaction logic for AddProfileDialog.xaml
/// </summary>
public partial class PreferencesDialog : MetroWindow, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PreferencesDialog(AppSetting appSetting)
{
InitializeComponent();
AppSetting = appSetting;
this.DataContext = this;
ApplicationSettingsVisibility = Visibility.Visible;
PopOutSettingsVisibility = Visibility.Collapsed;
AutoPopOutSettingsVisibility = Visibility.Collapsed;
}
public AppSetting AppSetting { get; set; }
public Visibility ApplicationSettingsVisibility { get; set; }
public Visibility PopOutSettingsVisibility { get; set; }
public Visibility AutoPopOutSettingsVisibility { get; set; }
private void TreeViewItem_Selected(object sender, RoutedEventArgs e)
{
var treeViewItem = (TreeViewItem)e.Source;
ApplicationSettingsVisibility = Visibility.Collapsed;
PopOutSettingsVisibility = Visibility.Collapsed;
AutoPopOutSettingsVisibility = Visibility.Collapsed;
if (treeViewItem.Header.ToString() == "Application Settings")
{
ApplicationSettingsVisibility = Visibility.Visible;
}
else if(treeViewItem.Header.ToString() == "Pop Out Settings")
{
PopOutSettingsVisibility = Visibility.Visible;
}
else if (treeViewItem.Header.ToString() == "Auto Pop Out Settings")
{
AutoPopOutSettingsVisibility = Visibility.Visible;
}
}
}
}

View file

@ -121,7 +121,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Left" Width="100" BorderThickness="0" Text="{Binding Path=Left, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Left" Width="100" BorderThickness="0" Text="{Binding Path=Left, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -129,7 +129,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Top" Width="100" BorderThickness="0" Text="{Binding Path=Top, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Top" Width="100" BorderThickness="0" Text="{Binding Path=Top, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -137,7 +137,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Width" Width="100" BorderThickness="0" Text="{Binding Path=Width, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Width" Width="100" BorderThickness="0" Text="{Binding Path=Width, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -145,7 +145,7 @@
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<TextBox Name="Height" Width="100" BorderThickness="0" Text="{Binding Path=Height, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}" <TextBox Name="Height" Width="100" BorderThickness="0" Text="{Binding Path=Height, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=LostFocus}"
SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" IsEnabled="{c:Binding Path='!HideTitlebar', Mode=TwoWay}" /> SourceUpdated="GridData_SourceUpdated" Style="{StaticResource TextBoxColumnFocus}" IsReadOnly="{c:Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=DataGrid, AncestorLevel=1}, Path='DataContext.DataStore.ActiveUserProfile.IsLocked or !DataContext.DataStore.AllowEdit'}" />
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
@ -157,7 +157,7 @@
</DataTemplate> </DataTemplate>
</DataGridTemplateColumn.CellTemplate> </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> </DataGridTemplateColumn>
<DataGridTemplateColumn Header="Hide Titlebar" Width="100"> <DataGridTemplateColumn Header="Hide Title Bar" Width="100">
<DataGridTemplateColumn.CellTemplate> <DataGridTemplateColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<CheckBox Name="HideTitlebar" Width="100" Margin="40 0 0 0" IsChecked="{Binding Path=HideTitlebar, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}" <CheckBox Name="HideTitlebar" Width="100" Margin="40 0 0 0" IsChecked="{Binding Path=HideTitlebar, Mode=TwoWay, NotifyOnSourceUpdated=True, UpdateSourceTrigger=PropertyChanged}"

View file

@ -27,23 +27,18 @@
</Style.Triggers> </Style.Triggers>
</Style> </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="Content" Value="Add Binding"/>
<Setter Property="Width" Value="130"/> <Setter Property="Width" Value="130"/>
<Setter Property="IsEnabled" Value="False"/> <Setter Property="IsEnabled" Value="False"/>
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.HasCurrentMsfsPlaneTitle and DataStore.ActiveUserProfile.BindingPlaneTitle != DataStore.CurrentMsfsPlaneTitle', Mode=OneWay}" Value="True"> <DataTrigger Binding="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.HasCurrentMsfsPlaneTitle and DataStore.IsAllowedAddAircraftBinding', Mode=OneWay}" Value="True">
<Setter Property="IsEnabled" Value="True"/> <Setter Property="IsEnabled" Value="True"/>
</DataTrigger> </DataTrigger>
<DataTrigger Binding="{c:Binding Path='DataStore.ActiveUserProfile.HasBindingPlaneTitle and DataStore.ActiveUserProfile.BindingPlaneTitle != DataStore.CurrentMsfsPlaneTitle', Mode=OneWay}" Value="True">
<Setter Property="Content" Value="Replace Binding"/>
<Setter Property="Width" Value="130"/>
</DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
<Style x:Key="ProfileDeletePlaneBindingDependency" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}"> <Style x:Key="ProfileDeletePlaneBindingDependency" TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="IsEnabled" Value="False"/> <Setter Property="IsEnabled" Value="False"/>
<Style.Triggers> <Style.Triggers>
<DataTrigger Binding="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.ActiveUserProfile.HasBindingPlaneTitle', Mode=OneWay}" Value="True"> <DataTrigger Binding="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.ActiveUserProfile.HasBindingPlaneTitle and DataStore.IsAllowedDeleteAircraftBinding', Mode=OneWay}" Value="True">
<Setter Property="IsEnabled" Value="True"/> <Setter Property="IsEnabled" Value="True"/>
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
@ -53,42 +48,71 @@
<DockPanel> <DockPanel>
<WrapPanel DockPanel.Dock="Left" Orientation="Vertical" Margin="15,10,0,0" Width="585" HorizontalAlignment="Left"> <WrapPanel DockPanel.Dock="Left" Orientation="Vertical" Margin="15,10,0,0" Width="585" HorizontalAlignment="Left">
<Label Content="1. Please select a profile you would like to use." HorizontalAlignment="Left" /> <Label Content="1. Please select a profile you would like to use." HorizontalAlignment="Left" />
<ComboBox HorizontalAlignment="Left" <WrapPanel Orientation="Horizontal" Margin="20,5,0,0" HorizontalAlignment="Left">
Margin="20,0,0,0" <ComboBox HorizontalAlignment="Left"
VerticalAlignment="Top" VerticalAlignment="Center"
Width="485" Width="450"
ItemsSource="{Binding Source={StaticResource UserProfilesViewSource}}" ItemsSource="{Binding Source={StaticResource UserProfilesViewSource}}"
SelectedValue="{Binding Path=DataStore.ActiveUserProfileId}" SelectedValue="{Binding Path=DataStore.ActiveUserProfileId}"
DisplayMemberPath="ProfileName" DisplayMemberPath="ProfileName"
SelectedValuePath="ProfileId"/> SelectedValuePath="ProfileId"/>
<WrapPanel Orientation="Horizontal" Margin="20,10,0,0" HorizontalAlignment="Left"> <Button Content="+" ToolTip="Add Profile" HorizontalAlignment="Left" Margin="10,0,0,0" Width="40" Click="AddProfile_Click" />
<Button Content="Add Profile" HorizontalAlignment="Left" Width="130" Click="AddProfile_Click"/> <Button Content="-" ToolTip="Delete Profile" HorizontalAlignment="Left" Margin="10,0,0,0" Width="40" Click="DeleteProfile_Click" Style="{StaticResource ProfileSelectedDependency}"/>
<Button Content="Delete Profile" HorizontalAlignment="Left" Margin="20,0,0,0" Width="130" Click="DeleteProfile_Click" Style="{StaticResource ProfileSelectedDependency}"/> <!--<Button Content="D" ToolTip="Set Default Profile" HorizontalAlignment="Left" Margin="10,0,0,0" Width="40" Command="{Binding Path=SetDefaultProfileCommand}" Style="{StaticResource ProfileSelectedDependency}"/>-->
<Button Content="Set Default" HorizontalAlignment="Left" Margin="20,0,0,0" Width="130" Command="{Binding Path=SetDefaultProfileCommand}" Style="{StaticResource ProfileSelectedDependency}"/>
</WrapPanel> </WrapPanel>
<WrapPanel Orientation="Horizontal" Margin="15,10,0,0" HorizontalAlignment="Left"> <Separator Margin="5,10,5,10"/>
<Label Content="Binding:" HorizontalAlignment="Left"/> <Label Content="2. Optional: Bind active aircraft livery to profile to automatically pop out panels." HorizontalAlignment="Left" Margin="0,0,0,0" />
<Label Content="{c:Binding Path='(DataStore.ActiveUserProfile.HasBindingPlaneTitle ? DataStore.ActiveUserProfile.BindingPlaneTitle : &quot;None&quot;)'}" HorizontalContentAlignment="Left" HorizontalAlignment="Left" FontStyle="Italic" Foreground="LightGreen" /> <WrapPanel Orientation="Vertical" Margin="15,0,0,0" HorizontalAlignment="Left">
</WrapPanel> <WrapPanel Orientation="Horizontal" Margin="5,5,0,0" HorizontalAlignment="Left">
<WrapPanel Orientation="Horizontal" Margin="20,5,0,0" HorizontalAlignment="Left"> <Label Content="{c:Binding Path='DataStore.CurrentMsfsPlaneTitle == null ? &quot;Active aircraft livery is unavailable&quot; : DataStore.CurrentMsfsPlaneTitle'}" HorizontalContentAlignment="Left" HorizontalAlignment="Left" FontStyle="Italic" Width="450">
<Button HorizontalAlignment="Left" Click="AddBinding_Click" Style="{StaticResource ProfileAddPlaneBindingDependency}"/> <Label.Style>
<Button Content="Delete Binding" HorizontalAlignment="Left" Margin="20,0,0,0" Width="130" Click="DeleteBinding_Click" Style="{StaticResource ProfileDeletePlaneBindingDependency}"/> <Style TargetType="{x:Type Label}">
<CheckBox Margin="40,0,0,0" IsChecked="{Binding Path=DataStore.ActiveUserProfile.PowerOnRequiredForColdStart}" IsEnabled="{Binding Path=DataStore.HasActiveUserProfileId}" Command="{Binding Path=SetPowerOnRequiredCommand}"> <Style.Triggers>
<TextBlock Text="Power on required to pop out panels on cold start" TextWrapping="Wrap" Width="200" Margin="5,0,0,3"/> <DataTrigger Binding="{c:Binding Path='DataStore.IsAircraftBindedToProfile'}" Value="True">
<Setter Property="Foreground" Value="LightGreen" ></Setter>
</DataTrigger>
<DataTrigger Binding="{c:Binding Path='DataStore.IsAircraftBindedToProfile'}" Value="False">
<Setter Property="Foreground" Value="AntiqueWhite" ></Setter>
</DataTrigger>
<DataTrigger Binding="{c:Binding Path='!DataStore.IsAllowedAddAircraftBinding and !DataStore.IsAllowedDeleteAircraftBinding'}" Value="True">
<Setter Property="Foreground" Value="Red" ></Setter>
<Setter Property="ToolTip" Value="Aircraft Livery is currently binded to another profile"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</Label.Style>
</Label>
<Button Content="+" ToolTip="Add Bidning" 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}"/>
</WrapPanel>
<CheckBox Margin="10,5,0,0" IsChecked="{Binding Path=DataStore.ActiveUserProfile.PowerOnRequiredForColdStart}" IsEnabled="{Binding Path=DataStore.HasActiveUserProfileId}" Command="{Binding Path=SetPowerOnRequiredCommand}">
<TextBlock Text="Power on required to pop out panels on cold start" TextWrapping="Wrap" Margin="5,0,0,3"/>
</CheckBox> </CheckBox>
</WrapPanel> </WrapPanel>
<Label Content="2. Identify pop out panel locations in the game by clicking on them." Margin="0,15,0,0" /> <Separator Margin="5,10,5,10"/>
<Label Content="3. Identify pop out panel locations in the game by clicking on them." Margin="0,0,0,0" />
<WrapPanel Orientation="Vertical" Margin="20,0,0,0" HorizontalAlignment="Left"> <WrapPanel Orientation="Vertical" Margin="20,0,0,0" HorizontalAlignment="Left">
<Label Content="LEFT CLICK to add a new panel."/> <WrapPanel Orientation="Horizontal">
<Label Content="CTRL + LEFT CLICK when all panels have been selected or to cancel selections." /> <Label Content="LEFT CLICK" Foreground="AntiqueWhite"/>
<Label Content="SHIFT + LEFT CLICK to remove the most recently added panel."/> <Label Content="to add a new panel."/>
<WrapPanel Orientation="Horizontal" Margin="0,10,0,0" HorizontalAlignment="Left"> </WrapPanel>
<WrapPanel Orientation="Horizontal">
<Label Content="CTRL + LEFT CLICK" Foreground="AntiqueWhite" />
<Label Content="when all panels have been selected or to cancel selections." />
</WrapPanel>
<WrapPanel Orientation="Horizontal">
<Label Content="SHIFT + LEFT CLICK" Foreground="AntiqueWhite" />
<Label Content="to remove the most recently added panel."/>
</WrapPanel>
<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="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}"/>
</WrapPanel> </WrapPanel>
</WrapPanel> </WrapPanel>
<Label Content="3. Start the pop out process for selected panels." Margin="0,15,0,0" /> <Separator Margin="5,10,5,10"/>
<Button Content="Start Pop Out" HorizontalAlignment="Left" Margin="20,10,0,0" Width="130" IsEnabled="{c:Binding Path='DataStore.HasActiveUserProfileId and DataStore.IsFlightActive'}" Command="{Binding Path=StartPopOutCommand}" Style="{StaticResource ProfileSelectedDependency}"/> <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}"/>
</WrapPanel> </WrapPanel>
<DockPanel DockPanel.Dock="Right" Width="325" HorizontalAlignment="Center"> <DockPanel DockPanel.Dock="Right" Width="325" HorizontalAlignment="Center">
<Label DockPanel.Dock="Top" Content="Panel Locations" HorizontalAlignment="Center" Margin="0,10,0,0"/> <Label DockPanel.Dock="Top" Content="Panel Locations" HorizontalAlignment="Center" Margin="0,10,0,0"/>

View file

@ -88,7 +88,7 @@ namespace MSFSPopoutPanelManager.WpfApp
private void AddBinding_Click(object sender, RoutedEventArgs e) private void AddBinding_Click(object sender, RoutedEventArgs e)
{ {
ConfirmationDialog dialog = new ConfirmationDialog("Confirm Add Binding", $"Are you sure you want to bind the selected profile to the following plane? \n{_panelSelectionViewModel.DataStore.CurrentMsfsPlaneTitle}"); ConfirmationDialog dialog = new ConfirmationDialog("Confirm Add Binding", $"Are you sure you want to bind the aircraft livery below to the active profile? \n{_panelSelectionViewModel.DataStore.CurrentMsfsPlaneTitle}");
dialog.Owner = Application.Current.MainWindow; dialog.Owner = Application.Current.MainWindow;
dialog.Topmost = true; dialog.Topmost = true;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner; dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;
@ -101,7 +101,7 @@ namespace MSFSPopoutPanelManager.WpfApp
private void DeleteBinding_Click(object sender, RoutedEventArgs e) private void DeleteBinding_Click(object sender, RoutedEventArgs e)
{ {
ConfirmationDialog dialog = new ConfirmationDialog("Confirm Delete Binding", $"Are you sure you want to delete the following binding for the selected profile? \n{_panelSelectionViewModel.DataStore.ActiveUserProfile.BindingPlaneTitle}"); ConfirmationDialog dialog = new ConfirmationDialog("Confirm Delete Binding", $"Are you sure you want to delete aircraft livery binding below from the active profile? \n{_panelSelectionViewModel.DataStore.CurrentMsfsPlaneTitle}");
dialog.Owner = Application.Current.MainWindow; dialog.Owner = Application.Current.MainWindow;
dialog.Topmost = true; dialog.Topmost = true;
dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner; dialog.WindowStartupLocation = WindowStartupLocation.CenterOwner;

View file

@ -63,7 +63,13 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
_simConnectManager = new SimConnectManager(); _simConnectManager = new SimConnectManager();
_simConnectManager.OnSimConnectDataRefreshed += (sender, e) => _simConnectManager.OnSimConnectDataRefreshed += (sender, e) =>
{ {
DataStore.CurrentMsfsPlaneTitle = e.Value.Title; // Automatic switching of active profile when SimConnect active aircraft livery changes
if(DataStore.CurrentMsfsPlaneTitle != e.Value.Title)
{
DataStore.CurrentMsfsPlaneTitle = e.Value.Title;
AutoSwitchProfile(e.Value.Title);
}
DataStore.ElectricalMasterBatteryStatus = e.Value.ElectricalMasterBattery; DataStore.ElectricalMasterBatteryStatus = e.Value.ElectricalMasterBattery;
}; };
_simConnectManager.OnConnected += (sender, e) => { DataStore.IsSimulatorStarted = true; }; _simConnectManager.OnConnected += (sender, e) => { DataStore.IsSimulatorStarted = true; };
@ -242,41 +248,43 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
{ {
Debug.WriteLine("Flight Started"); Debug.WriteLine("Flight Started");
DataStore.IsEnteredFlight = true; Application.Current.Dispatcher.Invoke(() =>
ShowPanelSelection(true);
// find the profile with the matching binding plane title
var profile = DataStore.UserProfiles.FirstOrDefault(p => p.BindingPlaneTitle == DataStore.CurrentMsfsPlaneTitle);
if (profile != null)
{ {
Thread.Sleep(DataStore.AppSetting.AutoPopOutPanelsWaitDelay.ReadyToFlyButton * 1000); // Wait for the ready to fly button AutoSwitchProfile(DataStore.CurrentMsfsPlaneTitle);
DataStore.IsEnteredFlight = true;
ShowPanelSelection(true);
// find the profile with the matching binding plane title
var profile = DataStore.UserProfiles.FirstOrDefault(p => p.BindingPlaneTitle.ToList().Exists(p => p == DataStore.CurrentMsfsPlaneTitle));
if (profile == null || profile.PanelSourceCoordinates.Count == 0)
return;
var messageDialog = new OnScreenMessageDialog($"Automatic pop out is starting for profile:\n{profile.ProfileName}", DataStore.AppSetting.AutoPopOutPanelsWaitDelay.ReadyToFlyButton); // Wait for the ready to fly button
messageDialog.ShowDialog();
InputEmulationManager.LeftClickReadyToFly(); InputEmulationManager.LeftClickReadyToFly();
Application.Current.Dispatcher.Invoke(() => Thread.Sleep(DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView * 1000); // Wait for the initial cockpit view
{
var messageDialog = new OnScreenMessageDialog($"Panel pop out in progress for profile:\n{profile.ProfileName}", DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InitialCockpitView);
messageDialog.ShowDialog();
// Turn on power if required to pop out panels // Turn on power if required to pop out panels
_simConnectManager.TurnOnPower(profile.PowerOnRequiredForColdStart); _simConnectManager.TurnOnPower(profile.PowerOnRequiredForColdStart);
Thread.Sleep(DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InstrumentationPowerOn * 1000); // Wait for battery to be turned on Thread.Sleep(DataStore.AppSetting.AutoPopOutPanelsWaitDelay.InstrumentationPowerOn * 1000); // Wait for battery to be turned on
DataStore.ActiveUserProfileId = profile.ProfileId; DataStore.ActiveUserProfileId = profile.ProfileId;
_panelPopoutManager.UserProfile = profile; _panelPopoutManager.UserProfile = profile;
_panelPopoutManager.AppSetting = DataStore.AppSetting; _panelPopoutManager.AppSetting = DataStore.AppSetting;
_panelPopoutManager.StartPopout(); _panelPopoutManager.StartPopout();
// Turn off power if needed after pop out // Turn off power if needed after pop out
_simConnectManager.TurnOffpower(); _simConnectManager.TurnOffpower();
}); });
}
} }
private void HandleOnFlightStopped(object sender, EventArgs e) private void HandleOnFlightStopped(object sender, EventArgs e)
{ {
DataStore.IsEnteredFlight = false; DataStore.IsEnteredFlight = false;
OnRestart(null);
} }
private void CheckForAutoUpdate() private void CheckForAutoUpdate()
@ -285,10 +293,21 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath); AutoUpdater.PersistenceProvider = new JsonFilePersistenceProvider(jsonPath);
AutoUpdater.Synchronous = true; AutoUpdater.Synchronous = true;
AutoUpdater.AppTitle = "MSFS Pop Out Panel Manager"; AutoUpdater.AppTitle = "MSFS Pop Out Panel Manager";
AutoUpdater.RunUpdateAsAdmin = false; //AutoUpdater.RunUpdateAsAdmin = false;
AutoUpdater.UpdateFormSize = new System.Drawing.Size(930, 675); AutoUpdater.UpdateFormSize = new System.Drawing.Size(930, 675);
//AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml"); AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml");
AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/AutoUpdateTest/main/autoupdate.xml"); //AutoUpdater.Start("https://raw.githubusercontent.com/hawkeye-stan/AutoUpdateTest/main/autoupdate.xml");
}
private void AutoSwitchProfile(string activeAircraftTitle)
{
// Automatic switching of active profile when SimConnect active aircraft livery changes
if (DataStore.UserProfiles != null)
{
var matchedProfile = DataStore.UserProfiles.ToList().Find(p => p.BindingPlaneTitle.ToList().Exists(t => t == activeAircraftTitle));
if (matchedProfile != null)
DataStore.ActiveUserProfileId = matchedProfile.ProfileId;
}
} }
} }
} }

View file

@ -117,6 +117,47 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
get { return !String.IsNullOrEmpty(CurrentMsfsPlaneTitle); } get { return !String.IsNullOrEmpty(CurrentMsfsPlaneTitle); }
} }
public bool IsAircraftBindedToProfile
{
get
{
if (ActiveUserProfile == null)
return false;
return ActiveUserProfile.BindingPlaneTitle.ToList().Exists(p => p == CurrentMsfsPlaneTitle);
}
}
public bool IsAllowedDeleteAircraftBinding
{
get
{
if (ActiveUserProfile == null || !HasCurrentMsfsPlaneTitle)
return false;
var uProfile = UserProfiles.ToList().Find(u => u.BindingPlaneTitle.ToList().Exists(p => p == CurrentMsfsPlaneTitle));
if (uProfile != null && uProfile.ProfileId != ActiveUserProfileId)
return false;
return ActiveUserProfile.BindingPlaneTitle.ToList().Exists(p => p == CurrentMsfsPlaneTitle);
}
}
public bool IsAllowedAddAircraftBinding
{
get
{
if (ActiveUserProfile == null || !HasCurrentMsfsPlaneTitle)
return false;
var uProfile = UserProfiles.ToList().Find(u => u.BindingPlaneTitle.ToList().Exists(p => p == CurrentMsfsPlaneTitle));
if (uProfile != null && uProfile.ProfileId != ActiveUserProfileId)
return false;
return !ActiveUserProfile.BindingPlaneTitle.ToList().Exists(p => p == CurrentMsfsPlaneTitle);
}
}
public bool ElectricalMasterBatteryStatus { get; set; } public bool ElectricalMasterBatteryStatus { get; set; }
public bool IsSimulatorStarted { get; set; } public bool IsSimulatorStarted { get; set; }

View file

@ -97,12 +97,24 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
private void OnAddProfileBinding(object commandParameter) private void OnAddProfileBinding(object commandParameter)
{ {
var index = DataStore.ActiveUserProfileId;
_userProfileManager.AddProfileBinding(DataStore.CurrentMsfsPlaneTitle, DataStore.ActiveUserProfileId); _userProfileManager.AddProfileBinding(DataStore.CurrentMsfsPlaneTitle, DataStore.ActiveUserProfileId);
// force profile refresh
DataStore.ActiveUserProfileId = -1;
DataStore.ActiveUserProfileId = index;
} }
private void OnDeleteProfileBinding(object commandParameter) private void OnDeleteProfileBinding(object commandParameter)
{ {
_userProfileManager.DeleteProfileBinding(DataStore.ActiveUserProfileId); var index = DataStore.ActiveUserProfileId;
_userProfileManager.DeleteProfileBinding(DataStore.CurrentMsfsPlaneTitle, DataStore.ActiveUserProfileId);
// force profile refresh
DataStore.ActiveUserProfileId = -1;
DataStore.ActiveUserProfileId = index;
} }
private void OnStartPanelSelection(object commandParameter) private void OnStartPanelSelection(object commandParameter)
@ -150,7 +162,7 @@ namespace MSFSPopoutPanelManager.WpfApp.ViewModel
var simualatorProcess = DiagnosticManager.GetSimulatorProcess(); var simualatorProcess = DiagnosticManager.GetSimulatorProcess();
if (simualatorProcess != null && DataStore.IsFlightActive) if (simualatorProcess != null && DataStore.IsFlightActive)
{ {
InputEmulationManager.SaveCustomViewZero(simualatorProcess.Handle); InputEmulationManager.SaveCustomView(simualatorProcess.Handle, DataStore.AppSetting.AutoPanningKeyBinding);
Logger.LogStatus("Auto Panning Camera has been saved succesfully.", StatusMessageType.Info); Logger.LogStatus("Auto Panning Camera has been saved succesfully.", StatusMessageType.Info);
} }
} }

View file

@ -4,7 +4,7 @@
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net5.0-windows</TargetFramework> <TargetFramework>net5.0-windows</TargetFramework>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
<Version>3.2.0.0</Version> <Version>3.3.0.0</Version>
<PackageId>MSFS 2020 Popout Panel Manager</PackageId> <PackageId>MSFS 2020 Popout Panel Manager</PackageId>
<Authors>Stanley Kwok</Authors> <Authors>Stanley Kwok</Authors>
<Product>MSFS 2020 Popout Panel Manager</Product> <Product>MSFS 2020 Popout Panel Manager</Product>
@ -75,12 +75,19 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Update="PreferencesDialog.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="UserControlPanelConfiguration.xaml.cs"> <Compile Update="UserControlPanelConfiguration.xaml.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Update="PreferencesDialog.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="UserControlPanelConfiguration.xaml"> <Page Update="UserControlPanelConfiguration.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime> <XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType> <SubType>Designer</SubType>

View file

@ -1,17 +1,12 @@
- Added per monitor DPI-awareness support. The application should run and display correctly when using combination of mixed monitor (with high-DPI and low-DPI) resolutions and scaling. Version 3.2.0.1 (v3.2 Release)
- Added system tray icon access. Application can start minimize or minimize to system tray. System tray icon features a context menu to allow quick access to application functions. - Added application auto update support. By installing this version of the app, auto update
functionality will be available for all future versions of the application.
- Added user requested feature to provide keyboard shortcut (Ctrl-Alt-P) to start panel pop out with either an active profile or a default profile selected. - Disabled panel adjustments when Hide Title Bar is checked for a panel. This is to fix an
issue where panel adjustments (X-Pos, Y-Pos, Width, and Height) will behave erratically when
Hide Title Bar is checked.
- New copy profile feature. You can reuse your defined panel settings for another plane or plane/livery combination. - Increased default delay for auto-clicking "Ready to Fly" button from 2 seconds to 4 seconds
in regard to Auto Pop Out Panels feature.
- Added quick panel location selection adjustment feature. You can now adjust panel locations without redoing the entire profile. [Fixed Issue #9] (https://github.com/hawkeye-stan/msfs-popout-panel-manager/issues/9
- Added Save Auto Panning Camera Angle function if you need to adjust the in-game camera angle during panel selection.
- Added application auto update feature.
- New logo icon for the app.
- New dark theme for the entire UI.