From 9d18b1fce46d1de8ddfe35bf13efa12b0f6aa9f4 Mon Sep 17 00:00:00 2001 From: hawkeye Date: Tue, 27 Feb 2024 21:44:21 -0500 Subject: [PATCH] Development --- DomainModel/DataFile/AppSettingFile.cs | 11 + DomainModel/DataFile/UserProfileFile.cs | 10 +- DomainModel/DomainModel.csproj | 6 +- DomainModel/DynamicLod/LodConfig.cs | 11 + .../ObservableLodConfigLinkedList.cs | 72 ++ DomainModel/Profile/FixedCameraConfig.cs | 21 + DomainModel/Profile/HudBarConfig.cs | 10 +- DomainModel/Profile/MonitorInfo.cs | 12 +- DomainModel/Profile/NumPadConfig.cs | 9 + DomainModel/Profile/PanelConfig.cs | 61 +- DomainModel/Profile/PanelConfigColors.cs | 2 +- DomainModel/Profile/PanelConfigItem.cs | 7 +- DomainModel/Profile/PanelType.cs | 1 + DomainModel/Profile/ProfileSetting.cs | 9 +- DomainModel/Profile/RefocusOnDisplay.cs | 12 +- DomainModel/Profile/UserProfile.cs | 84 ++- DomainModel/Setting/AfterPopOutCameraView.cs | 16 +- DomainModel/Setting/AppAutoStart.cs | 100 +-- DomainModel/Setting/ApplicationSetting.cs | 40 +- DomainModel/Setting/AutoPanning.cs | 10 +- DomainModel/Setting/AutoPopOutSetting.cs | 10 +- DomainModel/Setting/DynamicLodSetting.cs | 71 ++ DomainModel/Setting/GeneralSetting.cs | 24 +- .../Setting/KeyboardShortcutSetting.cs | 10 +- DomainModel/Setting/PopOutSetting.cs | 29 +- .../Setting/PopOutTitleBarCustomization.cs | 10 +- DomainModel/Setting/RefocusGameWindow.cs | 10 +- DomainModel/Setting/RefocusSetting.cs | 4 +- DomainModel/Setting/SystemSetting.cs | 10 +- DomainModel/Setting/TouchSetting.cs | 7 +- DomainModel/Setting/TrackIRSetting.cs | 7 +- DomainModel/Setting/WindowedModeSetting.cs | 7 +- DomainModel/SimConnect/HudBarData737.cs | 14 +- DomainModel/SimConnect/HudBarDataGeneric.cs | 14 +- MainApp/App.xaml.cs | 110 +-- .../Dialog/AddProfileDialog.xaml | 92 +++ .../Dialog/AddProfileDialog.xaml.cs | 29 + .../Dialog/ConfirmationDialog.xaml | 56 ++ .../Dialog/ConfirmationDialog.xaml.cs | 18 + .../AppUserControl/DynamicLodPreference.xaml | 556 +++++++++++++++ .../DynamicLodPreference.xaml.cs | 225 ++++++ MainApp/AppUserControl/HelpDrawer.xaml | 334 +++++++++ MainApp/AppUserControl/HelpDrawer.xaml.cs | 28 + .../EditPanelSourceButton.xaml | 25 + .../EditPanelSourceButton.xaml.cs | 10 + .../MoveAndResizePanelButton.xaml | 43 ++ .../MoveAndResizePanelButton.xaml.cs | 10 + .../PopOutPanelCard/MoveUpDownButton.xaml | 52 ++ .../PopOutPanelCard/MoveUpDownButton.xaml.cs | 10 + .../PopOutPanelCard/PanelConfigField.xaml | 145 ++++ .../PopOutPanelCard/PanelConfigField.xaml.cs | 112 +++ .../PopOutPanelCard/PanelTargetField.xaml | 84 +++ .../PopOutPanelCard/PanelTargetField.xaml.cs | 108 +++ .../PopOutPanelCard/TouchEnabledButton.xaml | 40 ++ .../TouchEnabledButton.xaml.cs | 10 + .../AppUserControl/PopOutPanelConfigCard.xaml | 337 +++++++++ .../PopOutPanelConfigCard.xaml.cs | 71 ++ MainApp/AppUserControl/PopOutPanelList.xaml | 139 ++++ .../AppUserControl/PopOutPanelList.xaml.cs | 27 + .../AppUserControl/PopOutPanelListEmpty.xaml | 46 ++ .../PopOutPanelListEmpty.xaml.cs | 10 + .../AppUserControl/PopOutPanelSourceCard.xaml | 138 ++++ .../PopOutPanelSourceCard.xaml.cs | 83 +++ .../PopOutPanelSourceLegacyCard.xaml | 124 ++++ .../PopOutPanelSourceLegacyCard.xaml.cs | 66 ++ MainApp/AppUserControl/PreferenceDrawer.xaml | 663 ++++++++++++++++++ .../AppUserControl/PreferenceDrawer.xaml.cs | 10 + MainApp/AppUserControl/ProfileCard.xaml | 540 ++++++++++++++ MainApp/AppUserControl/ProfileCard.xaml.cs | 83 +++ MainApp/AppUserControl/ProfileCardEmpty.xaml | 44 ++ .../AppUserControl/ProfileCardEmpty.xaml.cs | 10 + MainApp/AppUserControl/ProfileCardList.xaml | 167 +++++ .../AppUserControl/ProfileCardList.xaml.cs | 42 ++ MainApp/AppUserControl/TrayIcon.xaml | 55 ++ MainApp/AppUserControl/TrayIcon.xaml.cs | 35 + MainApp/AppWindow/AppMainWindow.xaml | 165 +++++ MainApp/AppWindow/AppMainWindow.xaml.cs | 102 +++ MainApp/AppWindow/HudBar.xaml | 284 ++++++++ MainApp/AppWindow/HudBar.xaml.cs | 57 ++ MainApp/AppWindow/MessageWindow.xaml | 53 ++ MainApp/AppWindow/MessageWindow.xaml.cs | 82 +++ MainApp/AppWindow/NumPad.xaml | 163 +++++ MainApp/AppWindow/NumPad.xaml.cs | 57 ++ MainApp/AppWindow/PanelCoorOverlay.xaml | 66 ++ MainApp/AppWindow/PanelCoorOverlay.xaml.cs | 110 +++ .../Converter/ExpanderRotateAngleConverter.cs | 34 + .../Converter/InverseBooleanOrConverter.cs | 15 +- MainApp/Converter/StringEmptyConverter.cs | 20 + MainApp/CustomControl/FontSizeConverter.cs | 6 +- MainApp/CustomControl/NumericUpDown.cs | 36 +- MainApp/MainApp.csproj | 8 +- .../PublishProfiles/FolderProfile.pubxml | 2 +- .../Styles/CustomMaterialDesignExpander.xaml | 7 +- MainApp/ViewModel/AddProfileViewModel.cs | 38 +- MainApp/ViewModel/ApplicationViewModel.cs | 30 +- MainApp/ViewModel/BaseViewModel.cs | 81 +-- MainApp/ViewModel/ConfirmationViewModel.cs | 2 +- MainApp/ViewModel/HelpViewModel.cs | 40 +- MainApp/ViewModel/HudBarViewModel.cs | 22 +- MainApp/ViewModel/MessageWindowViewModel.cs | 54 +- MainApp/ViewModel/NumPadViewModel.cs | 28 + MainApp/ViewModel/OrchestratorUIHelper.cs | 111 +-- .../ViewModel/PanelConfigFieldViewModel.cs | 20 +- .../ViewModel/PanelCoorOverlayViewModel.cs | 6 +- .../PopOutPanelConfigCardViewModel.cs | 209 ++++++ MainApp/ViewModel/PopOutPanelListViewModel.cs | 3 +- .../PopOutPanelSourceCardViewModel.cs | 76 ++ .../PopOutPanelSourceLegacyCardViewModel.cs | 48 ++ MainApp/ViewModel/ProfileCardListViewModel.cs | 35 +- MainApp/ViewModel/ProfileCardViewModel.cs | 189 +++-- MainApp/ViewModel/TrayIconViewModel.cs | 24 +- Orchestration/AppOrchestrator.cs | 76 ++ Orchestration/AppSettingData.cs | 25 +- Orchestration/AppSettingDataManager.cs | 61 +- Orchestration/BaseOrchestrator.cs | 20 + Orchestration/DynamicLodManager.cs | 257 +++++++ Orchestration/FlightSimData.cs | 23 +- Orchestration/FlightSimOrchestrator.cs | 262 +++---- Orchestration/HelpOrchestrator.cs | 30 +- Orchestration/KeyboardOrchestrator.cs | 25 +- Orchestration/Orchestration.csproj | 6 +- .../PanelConfigurationOrchestrator.cs | 57 +- Orchestration/PanelPopOutOrchestrator.cs | 274 ++++---- Orchestration/PanelSourceOrchestrator.cs | 283 +++++--- Orchestration/ProfileData.cs | 90 ++- Orchestration/ProfileDataManager.cs | 52 +- Orchestration/ProfileOrchestrator.cs | 68 +- Orchestration/SharedStorage.cs | 18 + Shared/FileLogger.cs | 6 +- Shared/ObjectExtension.cs | 47 +- Shared/ObservableObject.cs | 45 +- Shared/ObservableRangeCollection.cs | 51 ++ Shared/Shared.csproj | 6 +- Shared/SortedObservableCollection.cs | 25 +- Shared/StatusMessage.cs | 11 + Shared/StatusMessageType.cs | 3 +- Shared/StatusMessageWriter.cs | 26 +- SimconnectAgent/Enums.cs | 14 +- SimconnectAgent/SimConnectDataDefinition.cs | 4 +- SimconnectAgent/SimConnectEvent.cs | 6 +- SimconnectAgent/SimConnectProvider.cs | 186 ++--- SimconnectAgent/SimConnectStruct.cs | 2 +- SimconnectAgent/SimConnector.cs | 248 ++++--- SimconnectAgent/SimDataDefinitions.cs | 82 ++- SimconnectAgent/SimDataItem.cs | 1 - SimconnectAgent/SimconnectAgent.csproj | 6 +- WindowsAgent/GameRefocusManager.cs | 34 +- WindowsAgent/InputEmulationManager.cs | 238 +++---- WindowsAgent/PInvoke.cs | 83 +-- WindowsAgent/TouchEventManager.cs | 29 +- WindowsAgent/WindowActionManager.cs | 100 ++- WindowsAgent/WindowEventManager.cs | 66 +- WindowsAgent/WindowProcessManager.cs | 30 +- WindowsAgent/WindowsAgent.csproj | 8 +- 154 files changed, 8942 insertions(+), 1843 deletions(-) create mode 100644 DomainModel/DataFile/AppSettingFile.cs create mode 100644 DomainModel/DynamicLod/LodConfig.cs create mode 100644 DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs create mode 100644 DomainModel/Profile/FixedCameraConfig.cs create mode 100644 DomainModel/Profile/NumPadConfig.cs create mode 100644 DomainModel/Setting/DynamicLodSetting.cs create mode 100644 MainApp/AppUserControl/Dialog/AddProfileDialog.xaml create mode 100644 MainApp/AppUserControl/Dialog/AddProfileDialog.xaml.cs create mode 100644 MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml create mode 100644 MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml.cs create mode 100644 MainApp/AppUserControl/DynamicLodPreference.xaml create mode 100644 MainApp/AppUserControl/DynamicLodPreference.xaml.cs create mode 100644 MainApp/AppUserControl/HelpDrawer.xaml create mode 100644 MainApp/AppUserControl/HelpDrawer.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/EditPanelSourceButton.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/EditPanelSourceButton.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/MoveAndResizePanelButton.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/MoveAndResizePanelButton.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/MoveUpDownButton.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/MoveUpDownButton.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelConfigCard.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelConfigCard.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelList.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelList.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelListEmpty.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelListEmpty.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelSourceCard.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelSourceCard.xaml.cs create mode 100644 MainApp/AppUserControl/PopOutPanelSourceLegacyCard.xaml create mode 100644 MainApp/AppUserControl/PopOutPanelSourceLegacyCard.xaml.cs create mode 100644 MainApp/AppUserControl/PreferenceDrawer.xaml create mode 100644 MainApp/AppUserControl/PreferenceDrawer.xaml.cs create mode 100644 MainApp/AppUserControl/ProfileCard.xaml create mode 100644 MainApp/AppUserControl/ProfileCard.xaml.cs create mode 100644 MainApp/AppUserControl/ProfileCardEmpty.xaml create mode 100644 MainApp/AppUserControl/ProfileCardEmpty.xaml.cs create mode 100644 MainApp/AppUserControl/ProfileCardList.xaml create mode 100644 MainApp/AppUserControl/ProfileCardList.xaml.cs create mode 100644 MainApp/AppUserControl/TrayIcon.xaml create mode 100644 MainApp/AppUserControl/TrayIcon.xaml.cs create mode 100644 MainApp/AppWindow/AppMainWindow.xaml create mode 100644 MainApp/AppWindow/AppMainWindow.xaml.cs create mode 100644 MainApp/AppWindow/HudBar.xaml create mode 100644 MainApp/AppWindow/HudBar.xaml.cs create mode 100644 MainApp/AppWindow/MessageWindow.xaml create mode 100644 MainApp/AppWindow/MessageWindow.xaml.cs create mode 100644 MainApp/AppWindow/NumPad.xaml create mode 100644 MainApp/AppWindow/NumPad.xaml.cs create mode 100644 MainApp/AppWindow/PanelCoorOverlay.xaml create mode 100644 MainApp/AppWindow/PanelCoorOverlay.xaml.cs create mode 100644 MainApp/Converter/ExpanderRotateAngleConverter.cs create mode 100644 MainApp/Converter/StringEmptyConverter.cs create mode 100644 MainApp/ViewModel/NumPadViewModel.cs create mode 100644 MainApp/ViewModel/PopOutPanelConfigCardViewModel.cs create mode 100644 MainApp/ViewModel/PopOutPanelSourceCardViewModel.cs create mode 100644 MainApp/ViewModel/PopOutPanelSourceLegacyCardViewModel.cs create mode 100644 Orchestration/AppOrchestrator.cs create mode 100644 Orchestration/BaseOrchestrator.cs create mode 100644 Orchestration/DynamicLodManager.cs create mode 100644 Orchestration/SharedStorage.cs create mode 100644 Shared/ObservableRangeCollection.cs create mode 100644 Shared/StatusMessage.cs diff --git a/DomainModel/DataFile/AppSettingFile.cs b/DomainModel/DataFile/AppSettingFile.cs new file mode 100644 index 0000000..66baad9 --- /dev/null +++ b/DomainModel/DataFile/AppSettingFile.cs @@ -0,0 +1,11 @@ +using MSFSPopoutPanelManager.DomainModel.Setting; + +namespace MSFSPopoutPanelManager.DomainModel.DataFile +{ + public class AppSettingFile + { + public string FileVersion { get; set; } = "4.0"; + + public ApplicationSetting ApplicationSetting { get; set; } = new(); + } +} diff --git a/DomainModel/DataFile/UserProfileFile.cs b/DomainModel/DataFile/UserProfileFile.cs index 59e2467..f8730b7 100644 --- a/DomainModel/DataFile/UserProfileFile.cs +++ b/DomainModel/DataFile/UserProfileFile.cs @@ -5,14 +5,8 @@ namespace MSFSPopoutPanelManager.DomainModel.DataFile { public class UserProfileFile { - public UserProfileFile() - { - FileVersion = "4.0"; - Profiles = new List(); - } + public string FileVersion { get; set; } = "4.0"; - public string FileVersion { get; set; } - - public IList Profiles { get; set; } + public IList Profiles { get; set; } = new List(); } } diff --git a/DomainModel/DomainModel.csproj b/DomainModel/DomainModel.csproj index 102c996..c5e9ea9 100644 --- a/DomainModel/DomainModel.csproj +++ b/DomainModel/DomainModel.csproj @@ -11,9 +11,9 @@ https://github.com/hawkeye-stan/msfs-popout-panel-manager MSFSPopoutPanelManager.DomainModel x64 - 4.0.3.4 - 4.0.3.4 - 4.0.3.4 + 4.0.4.1 + 4.0.4.1 + 4.0.4.1 win-x64 Embedded Debug;Release;Local diff --git a/DomainModel/DynamicLod/LodConfig.cs b/DomainModel/DynamicLod/LodConfig.cs new file mode 100644 index 0000000..b20d484 --- /dev/null +++ b/DomainModel/DynamicLod/LodConfig.cs @@ -0,0 +1,11 @@ +using MSFSPopoutPanelManager.Shared; + +namespace MSFSPopoutPanelManager.DomainModel.DynamicLod +{ + public class LodConfig : ObservableObject + { + public int Agl { get; set; } + + public int Lod { get; set; } + } +} diff --git a/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs b/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs new file mode 100644 index 0000000..4c20b04 --- /dev/null +++ b/DomainModel/DynamicLod/ObservableLodConfigLinkedList.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace MSFSPopoutPanelManager.DomainModel.DynamicLod +{ + public class ObservableLodConfigLinkedList : LinkedList, INotifyCollectionChanged + { + public event NotifyCollectionChangedEventHandler CollectionChanged; + public void OnNotifyCollectionChanged(NotifyCollectionChangedAction action, LodConfig item) + { + if (CollectionChanged == null) + return; + + switch (action) + { + case NotifyCollectionChangedAction.Add: + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Reset: + CollectionChanged(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + break; + } + } + + public new void AddFirst(LodConfig item) + { + base.AddFirst(item); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item); + } + + public new void AddLast(LodConfig item) + { + base.AddLast(item); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, item); + } + + public new void AddBefore(LinkedListNode node, LinkedListNode newNode) + { + base.AddBefore(node, newNode); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value); + } + + public new void AddAfter(LinkedListNode node, LinkedListNode newNode) + { + base.AddAfter(node, newNode); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Add, newNode.Value); + } + + public new void Remove(LodConfig item) + { + base.Remove(item); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, item); + } + + public new void RemoveFirst() + { + if (First == null) + return; + + base.RemoveFirst(); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, First.Value); + } + + public new void RemoveLast() + { + if (Last == null) + return; + + base.RemoveLast(); + OnNotifyCollectionChanged(NotifyCollectionChangedAction.Remove, Last.Value); + } + } +} diff --git a/DomainModel/Profile/FixedCameraConfig.cs b/DomainModel/Profile/FixedCameraConfig.cs new file mode 100644 index 0000000..1b75df7 --- /dev/null +++ b/DomainModel/Profile/FixedCameraConfig.cs @@ -0,0 +1,21 @@ +using MSFSPopoutPanelManager.Shared; + +namespace MSFSPopoutPanelManager.DomainModel.Profile +{ + public class FixedCameraConfig : ObservableObject + { + public int Id { get; set; } = 0; + + public CameraType CameraType { get; set; } = CameraType.Cockpit; + + public int CameraIndex { get; set; } = 1; + + public string Name { get; set; } = "Cockpit Pilot"; + } + + public enum CameraType + { + Cockpit = 1, + Instrument = 2 + } +} diff --git a/DomainModel/Profile/HudBarConfig.cs b/DomainModel/Profile/HudBarConfig.cs index c197e4b..419eea2 100644 --- a/DomainModel/Profile/HudBarConfig.cs +++ b/DomainModel/Profile/HudBarConfig.cs @@ -4,15 +4,9 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public class HudBarConfig : ObservableObject { - public HudBarConfig() - { - IsEnabled = false; - HudBarType = HudBarType.Generic_Aircraft; - } + public bool IsEnabled { get; set; } = false; - public bool IsEnabled { get; set; } - - public HudBarType HudBarType { get; set; } + public HudBarType HudBarType { get; set; } = HudBarType.Generic_Aircraft; } public enum HudBarType diff --git a/DomainModel/Profile/MonitorInfo.cs b/DomainModel/Profile/MonitorInfo.cs index eb77579..b7cc9f9 100644 --- a/DomainModel/Profile/MonitorInfo.cs +++ b/DomainModel/Profile/MonitorInfo.cs @@ -6,21 +6,19 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public MonitorInfo() { - IsSelected = false; - InitializeChildPropertyChangeBinding(); } public string Name { get; set; } - public bool IsSelected { get; set; } + public bool IsSelected { get; set; } = false; - public int X { get; set; } + public int X { get; set; } = 0; - public int Y { get; set; } + public int Y { get; set; } = 0; - public int Width { get; set; } + public int Width { get; set; } = 0; - public int Height { get; set; } + public int Height { get; set; } = 0; } } diff --git a/DomainModel/Profile/NumPadConfig.cs b/DomainModel/Profile/NumPadConfig.cs new file mode 100644 index 0000000..ec47eb8 --- /dev/null +++ b/DomainModel/Profile/NumPadConfig.cs @@ -0,0 +1,9 @@ +using MSFSPopoutPanelManager.Shared; + +namespace MSFSPopoutPanelManager.DomainModel.Profile +{ + public class NumPadConfig : ObservableObject + { + public bool IsEnabled { get; set; } = false; + } +} diff --git a/DomainModel/Profile/PanelConfig.cs b/DomainModel/Profile/PanelConfig.cs index 50be405..878f2a0 100644 --- a/DomainModel/Profile/PanelConfig.cs +++ b/DomainModel/Profile/PanelConfig.cs @@ -11,11 +11,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile if (Id == Guid.Empty) Id = Guid.NewGuid(); - PanelName = "Default Panel Name"; - PanelHandle = IntPtr.MaxValue; - AutoGameRefocus = true; - PanelSource = new PanelSource(); - InitializeChildPropertyChangeBinding(); PropertyChanged += PanelConfig_PropertyChanged; @@ -38,7 +33,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile public PanelType PanelType { get; set; } - public string PanelName { get; set; } + public string PanelName { get; set; } = "Default Panel Name"; public int Top { get; set; } @@ -56,21 +51,18 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile public bool TouchEnabled { get; set; } - public bool AutoGameRefocus { get; set; } + public bool AutoGameRefocus { get; set; } = true; - public PanelSource PanelSource { get; set; } + public PanelSource PanelSource { get; set; } = new(); + + public FixedCameraConfig FixedCameraConfig { get; set; } = new(); [JsonIgnore] - public IntPtr PanelHandle { get; set; } + public IntPtr PanelHandle { get; set; } = IntPtr.MaxValue; [JsonIgnore] public bool IsEditingPanel { get; set; } - [JsonIgnore] - public bool IsCustomPopOut => PanelType == PanelType.CustomPopout; - - [JsonIgnore] - public bool IsBuiltInPopOut => PanelType == PanelType.BuiltInPopout; [JsonIgnore] public bool HasPanelSource => PanelType == PanelType.BuiltInPopout || (PanelType == PanelType.CustomPopout && PanelSource != null && PanelSource.X != null); @@ -83,10 +75,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile if (PanelHandle == IntPtr.MaxValue) return null; - if (PanelHandle == IntPtr.Zero) - return false; - - return true; + return PanelHandle != IntPtr.Zero; } } @@ -95,5 +84,41 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile [JsonIgnore] public bool IsShownPanelSource { get; set; } + + [JsonIgnore] + public bool IsDeletablePanel => PanelType != PanelType.HudBarWindow && PanelType != PanelType.RefocusDisplay && + PanelType != PanelType.NumPadWindow; + + [JsonIgnore] + public bool IsTouchEnablePanel => PanelType != PanelType.HudBarWindow && PanelType != PanelType.RefocusDisplay && + PanelType != PanelType.NumPadWindow; + + [JsonIgnore] + public bool IsCustomPopOut => PanelType == PanelType.CustomPopout; + + + [JsonIgnore] + public bool IsBuiltInPopOut => PanelType == PanelType.BuiltInPopout; + + [JsonIgnore] + public bool IsHudBarWindow => PanelType == PanelType.HudBarWindow; + + [JsonIgnore] + public bool IsRefocusDisplay => PanelType == PanelType.RefocusDisplay; + + [JsonIgnore] + public bool IsNumPadWindow => PanelType == PanelType.NumPadWindow; + + [JsonIgnore] + public string PanelSourceCoordinateText + { + get + { + if (PanelSource == null || PanelSource.X == null || PanelSource.Y == null) + return "top: N/A, left: N/A"; + + return $"Left: {PanelSource.X} / Top: {PanelSource.Y}"; + } + } } } diff --git a/DomainModel/Profile/PanelConfigColors.cs b/DomainModel/Profile/PanelConfigColors.cs index d59d8a0..89b0202 100644 --- a/DomainModel/Profile/PanelConfigColors.cs +++ b/DomainModel/Profile/PanelConfigColors.cs @@ -8,7 +8,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public static string GetNextAvailableColor(List panelConfigs) { - foreach (string colorName in Enum.GetNames()) + foreach (var colorName in Enum.GetNames()) { if (panelConfigs.Any(p => p.PanelSource.Color == colorName)) continue; diff --git a/DomainModel/Profile/PanelConfigItem.cs b/DomainModel/Profile/PanelConfigItem.cs index b9af5e4..15697ca 100644 --- a/DomainModel/Profile/PanelConfigItem.cs +++ b/DomainModel/Profile/PanelConfigItem.cs @@ -4,12 +4,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public class PanelConfigItem { - public PanelConfigItem() - { - PanelHandle = IntPtr.Zero; - } - - public IntPtr PanelHandle { get; set; } + public IntPtr PanelHandle { get; set; } = IntPtr.Zero; public PanelConfigPropertyName PanelConfigProperty { get; set; } } diff --git a/DomainModel/Profile/PanelType.cs b/DomainModel/Profile/PanelType.cs index 6c1f8cb..9fc1d9a 100644 --- a/DomainModel/Profile/PanelType.cs +++ b/DomainModel/Profile/PanelType.cs @@ -11,6 +11,7 @@ PanelSourceWindow = 6, StatusMessageWindow = 7, RefocusDisplay = 8, + NumPadWindow = 9, Unknown = 100 } } diff --git a/DomainModel/Profile/ProfileSetting.cs b/DomainModel/Profile/ProfileSetting.cs index 0edef7d..d5157ea 100644 --- a/DomainModel/Profile/ProfileSetting.cs +++ b/DomainModel/Profile/ProfileSetting.cs @@ -6,9 +6,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public ProfileSetting() { - HudBarConfig = new HudBarConfig(); - RefocusOnDisplay = new RefocusOnDisplay(); - InitializeChildPropertyChangeBinding(); } @@ -16,8 +13,10 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile public bool IncludeInGamePanels { get; set; } - public HudBarConfig HudBarConfig { get; set; } + public HudBarConfig HudBarConfig { get; set; } = new(); - public RefocusOnDisplay RefocusOnDisplay { get; set; } + public RefocusOnDisplay RefocusOnDisplay { get; set; } = new(); + + public NumPadConfig NumPadConfig { get; set; } = new(); } } diff --git a/DomainModel/Profile/RefocusOnDisplay.cs b/DomainModel/Profile/RefocusOnDisplay.cs index b2272f9..2412c08 100644 --- a/DomainModel/Profile/RefocusOnDisplay.cs +++ b/DomainModel/Profile/RefocusOnDisplay.cs @@ -8,7 +8,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public RefocusOnDisplay() { - IsEnabled = false; Monitors = new ObservableCollection(); Monitors.CollectionChanged += (sender, e) => @@ -16,14 +15,13 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile switch (e.Action) { case System.Collections.Specialized.NotifyCollectionChangedAction.Add: - if (e.NewItems[0] == null) + if (e.NewItems?[0] == null) return; - ((MonitorInfo)e.NewItems[0]).PropertyChanged += (sender, e) => + ((MonitorInfo)e.NewItems[0]).PropertyChanged += (innerSender, innerArg) => { - var evtArg = e as PropertyChangedExtendedEventArgs; - if (!evtArg.DisableSave) - base.OnPropertyChanged(sender, e); + if (innerArg is PropertyChangedExtendedEventArgs { DisableSave: false }) + base.OnPropertyChanged(innerSender, innerArg); }; base.OnPropertyChanged(sender, new PropertyChangedEventArgs("Monitors")); break; @@ -39,7 +37,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile InitializeChildPropertyChangeBinding(); } - public bool IsEnabled { get; set; } + public bool IsEnabled { get; set; } = false; public ObservableCollection Monitors { get; set; } diff --git a/DomainModel/Profile/UserProfile.cs b/DomainModel/Profile/UserProfile.cs index 9c59054..7657661 100644 --- a/DomainModel/Profile/UserProfile.cs +++ b/DomainModel/Profile/UserProfile.cs @@ -12,46 +12,38 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile { public UserProfile() { - Id = Guid.NewGuid(); - IsLocked = false; - - AircraftBindings = new ObservableCollection(); - PanelConfigs = new ObservableCollection(); - ProfileSetting = new ProfileSetting(); - MsfsGameWindowConfig = new MsfsGameWindowConfig(); - PanelSourceCockpitZoomFactor = 50; - - this.PropertyChanged += (sender, e) => + this.PropertyChanged += (_, e) => { - var evtArg = e as PropertyChangedExtendedEventArgs; - if (!evtArg.DisableSave) - ProfileChanged?.Invoke(this, null); + if (e is PropertyChangedExtendedEventArgs { DisableSave: false }) + OnProfileChanged?.Invoke(this, EventArgs.Empty); + + if (e.PropertyName == nameof(IsUsedLegacyCameraSystem)) + OnPanelConfigChanged(); }; - PanelConfigs.CollectionChanged += (sender, e) => + PanelConfigs.CollectionChanged += (_, e) => { switch (e.Action) { case System.Collections.Specialized.NotifyCollectionChangedAction.Add: - if (e.NewItems[0] == null) + if (e.NewItems?[0] == null) return; - ((PanelConfig)e.NewItems[0]).PropertyChanged += (sender, e) => + ((PanelConfig)e.NewItems[0]).PropertyChanged += (_, arg) => { - var evtArg = e as PropertyChangedExtendedEventArgs; - if (!evtArg.DisableSave) - ProfileChanged?.Invoke(this, null); + if (arg is PropertyChangedExtendedEventArgs { DisableSave: false }) + OnProfileChanged?.Invoke(this, EventArgs.Empty); - OnPanelConfigChanged(sender, e); + OnPanelConfigChanged(); }; - ProfileChanged?.Invoke(this, null); - OnPanelConfigChanged(sender, e); + OnProfileChanged?.Invoke(this, EventArgs.Empty); + OnPanelConfigChanged(); break; case System.Collections.Specialized.NotifyCollectionChangedAction.Remove: case System.Collections.Specialized.NotifyCollectionChangedAction.Move: case System.Collections.Specialized.NotifyCollectionChangedAction.Replace: - ProfileChanged?.Invoke(this, null); - OnPanelConfigChanged(sender, e); + OnProfileChanged?.Invoke(this, EventArgs.Empty); + OnPanelConfigChanged(); break; } }; @@ -59,30 +51,29 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile InitializeChildPropertyChangeBinding(); } - public event EventHandler ProfileChanged; + public event EventHandler OnProfileChanged; - public Guid Id { get; set; } + public Guid Id { get; set; } = Guid.NewGuid(); public string Name { get; set; } - public bool IsLocked { get; set; } + public bool IsLocked { get; set; } = false; - public ObservableCollection AircraftBindings { get; set; } + public ObservableCollection AircraftBindings { get; set; } = new(); - public ObservableCollection PanelConfigs { get; set; } + public ObservableCollection PanelConfigs { get; set; } = new(); - public ProfileSetting ProfileSetting { get; set; } + public ProfileSetting ProfileSetting { get; set; } = new(); - public MsfsGameWindowConfig MsfsGameWindowConfig { get; set; } + public MsfsGameWindowConfig MsfsGameWindowConfig { get; set; } = new(); - public int PanelSourceCockpitZoomFactor { get; set; } + public int PanelSourceCockpitZoomFactor { get; set; } = 50; + + public bool IsUsedLegacyCameraSystem { get; set; } = true; public int CompareTo(UserProfile other) { - int result = this.Name.ToLower().CompareTo(other.Name.ToLower()); - if (result == 0) - result = this.Name.ToLower().CompareTo(other.Name.ToLower()); - return result; + return string.Compare(Name.ToLower(), other.Name.ToLower(), StringComparison.Ordinal); } [JsonIgnore] @@ -91,20 +82,23 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile [JsonIgnore] public bool IsEditingPanelSource { get; set; } + [JsonIgnore] + public bool IsSelectingPanelSource { get; set; } + private bool _isPoppedOut; [JsonIgnore] public bool IsPoppedOut { - get { return _isPoppedOut; } + get => _isPoppedOut; set { _isPoppedOut = value; - if (!value) - { - foreach (var panelConfig in PanelConfigs) - panelConfig.PanelHandle = IntPtr.MaxValue; // reset panel is popped out status - } + if (value) + return; + + foreach (var panelConfig in PanelConfigs) + panelConfig.PanelHandle = IntPtr.MaxValue; // reset panel is popped out status } } @@ -115,12 +109,12 @@ namespace MSFSPopoutPanelManager.DomainModel.Profile public bool HasUnidentifiedPanelSource { get; private set; } [JsonIgnore] - public bool HasAircraftBindings => AircraftBindings != null && AircraftBindings.Count > 0; + public bool IsDisabledStartPopOut { get; set; } [JsonIgnore] - public bool HasCustomPanels => PanelConfigs.Count(p => p.PanelType == PanelType.CustomPopout) > 0; + public bool HasCustomPanels => PanelConfigs != null && PanelConfigs.Count(p => p.PanelType == PanelType.CustomPopout) > 0; - private void OnPanelConfigChanged(object sender, EventArgs e) + private void OnPanelConfigChanged() { HasUnidentifiedPanelSource = PanelConfigs.Any(p => p.PanelType == PanelType.CustomPopout && p.PanelSource.X == null); } diff --git a/DomainModel/Setting/AfterPopOutCameraView.cs b/DomainModel/Setting/AfterPopOutCameraView.cs index 5811aa8..77c5253 100644 --- a/DomainModel/Setting/AfterPopOutCameraView.cs +++ b/DomainModel/Setting/AfterPopOutCameraView.cs @@ -5,23 +5,15 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class AfterPopOutCameraView : ObservableObject { - public AfterPopOutCameraView() - { - // Default values - IsEnabled = true; - CameraView = AfterPopOutCameraViewType.CockpitCenterView; - KeyBinding = "1"; - } + public bool IsEnabled { get; set; } = true; - public bool IsEnabled { get; set; } + public AfterPopOutCameraViewType CameraView { get; set; } = AfterPopOutCameraViewType.CockpitCenterView; - public AfterPopOutCameraViewType CameraView { get; set; } - - public string KeyBinding { get; set; } + public string KeyBinding { get; set; } = "1"; // Use for MVVM binding only [JsonIgnore] - public bool IsEnabledCustomCameraKeyBinding { get { return IsEnabled && CameraView == AfterPopOutCameraViewType.CustomCameraView; } } + public bool IsEnabledCustomCameraKeyBinding => IsEnabled && CameraView == AfterPopOutCameraViewType.CustomCameraView; } public enum AfterPopOutCameraViewType diff --git a/DomainModel/Setting/AppAutoStart.cs b/DomainModel/Setting/AppAutoStart.cs index a6592b2..5bd0328 100644 --- a/DomainModel/Setting/AppAutoStart.cs +++ b/DomainModel/Setting/AppAutoStart.cs @@ -14,7 +14,7 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); var filePath = GetFilePath(); var autoStartArg = new LaunchAddOn() { Name = "MSFS Popout Panel Manager", Disabled = "false", Path = $@"{Directory.GetCurrentDirectory()}\MSFSPopoutPanelManager.exe" }; - XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument)); + var serializer = new XmlSerializer(typeof(SimBaseDocument)); if (filePath != null && File.Exists(filePath)) { @@ -26,32 +26,33 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting SimBaseDocument data; - using (Stream stream = new FileStream(filePath, FileMode.Open)) + using (var stream = new FileStream(filePath, FileMode.Open)) { data = (SimBaseDocument)serializer.Deserialize(stream); } - if (data != null) + if (data == null) + return; + + if (data.LaunchAddOn.Count == 0) { - if (data.LaunchAddOn.Count == 0) - { - data.LaunchAddOn.Add(autoStartArg); - } + data.LaunchAddOn.Add(autoStartArg); + } + else + { + var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager"); + + if (autoStartIndex > -1) + data.LaunchAddOn[autoStartIndex] = autoStartArg; else - { - var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager"); + data.LaunchAddOn.Add(autoStartArg); + } - if (autoStartIndex > -1) - data.LaunchAddOn[autoStartIndex] = autoStartArg; - else - data.LaunchAddOn.Add(autoStartArg); - } - - using (Stream stream = new FileStream(filePath, FileMode.Open)) - { - stream.SetLength(0); - serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); - } + using (var stream = new FileStream(filePath, FileMode.Open)) + { + stream.SetLength(0); + serializer.Serialize(stream, data, + new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); } } else @@ -61,15 +62,16 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting Descr = "SimConnect", Filename = "SimConnect.xml", Disabled = "False", - Type = "SimConnecct", + Type = "SimConnect", Version = "1,0", LaunchAddOn = new List() { autoStartArg } }; - using (var file = File.Create(filePath)) - { - serializer.Serialize(file, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); - } + if (filePath == null) + return; + + using var file = File.Create(filePath); + serializer.Serialize(file, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); } } @@ -77,32 +79,32 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { var filePath = GetFilePath(); - if (filePath != null && File.Exists(filePath)) + if (filePath == null || !File.Exists(filePath)) + return; + + SimBaseDocument data; + var serializer = new XmlSerializer(typeof(SimBaseDocument)); + + using (var stream = new FileStream(filePath, FileMode.Open)) { - SimBaseDocument data; - XmlSerializer serializer = new XmlSerializer(typeof(SimBaseDocument)); + data = (SimBaseDocument)serializer.Deserialize(stream); + } - using (Stream stream = new FileStream(filePath, FileMode.Open)) - { - data = (SimBaseDocument)serializer.Deserialize(stream); - } + if (data == null) + return; - if (data != null) - { - if (data.LaunchAddOn.Count > 0) - { - var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager"); + if (data.LaunchAddOn.Count > 0) + { + var autoStartIndex = data.LaunchAddOn.FindIndex(x => x.Name == "MSFS Popout Panel Manager"); - if (autoStartIndex > -1) - data.LaunchAddOn.RemoveAt(autoStartIndex); - } + if (autoStartIndex > -1) + data.LaunchAddOn.RemoveAt(autoStartIndex); + } - using (Stream stream = new FileStream(filePath, FileMode.Open)) - { - stream.SetLength(0); - serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); - } - } + using (var stream = new FileStream(filePath, FileMode.Open)) + { + stream.SetLength(0); + serializer.Serialize(stream, data, new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty })); } } @@ -141,11 +143,11 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting private static string GetFilePath() { - var filePathMSStore = Environment.ExpandEnvironmentVariables("%LocalAppData%") + @"\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache\"; + var filePathMsStore = Environment.ExpandEnvironmentVariables("%LocalAppData%") + @"\Packages\Microsoft.FlightSimulator_8wekyb3d8bbwe\LocalCache\"; var filePathSteam = Environment.ExpandEnvironmentVariables("%AppData%") + @"\Microsoft Flight Simulator\LocalCache\"; - if (Directory.Exists(filePathMSStore)) - return filePathMSStore + "exe.xml"; + if (Directory.Exists(filePathMsStore)) + return filePathMsStore + "exe.xml"; else if (Directory.Exists(filePathSteam)) return filePathSteam + "exe.xml"; else diff --git a/DomainModel/Setting/ApplicationSetting.cs b/DomainModel/Setting/ApplicationSetting.cs index 8277695..8b1dfc7 100644 --- a/DomainModel/Setting/ApplicationSetting.cs +++ b/DomainModel/Setting/ApplicationSetting.cs @@ -7,45 +7,35 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public ApplicationSetting() { - GeneralSetting = new GeneralSetting(); - AutoPopOutSetting = new AutoPopOutSetting(); - PopOutSetting = new PopOutSetting(); - RefocusSetting = new RefocusSetting(); - TouchSetting = new TouchSetting(); - TrackIRSetting = new TrackIRSetting(); - WindowedModeSetting = new WindowedModeSetting(); - SystemSetting = new SystemSetting(); - KeyboardShortcutSetting = new KeyboardShortcutSetting(); - InitializeChildPropertyChangeBinding(); - this.PropertyChanged += (sender, e) => + PropertyChanged += (_, e) => { - var evtArg = e as PropertyChangedExtendedEventArgs; - - if (evtArg.ObjectName == "MSFSPopoutPanelManager.DomainModel.Setting.KeyboardShortcutSetting" && evtArg.PropertyName == "IsEnabled") - IsUsedKeyboardShortcutChanged?.Invoke(this, KeyboardShortcutSetting.IsEnabled); + if (e is PropertyChangedExtendedEventArgs { ObjectName: "MSFSPopoutPanelManager.DomainModel.Setting.KeyboardShortcutSetting", PropertyName: "IsEnabled" }) + OnIsUsedKeyboardShortcutChanged?.Invoke(this, KeyboardShortcutSetting.IsEnabled); }; } - public GeneralSetting GeneralSetting { get; set; } + public GeneralSetting GeneralSetting { get; set; } = new(); - public AutoPopOutSetting AutoPopOutSetting { get; set; } + public AutoPopOutSetting AutoPopOutSetting { get; set; } = new(); - public PopOutSetting PopOutSetting { get; set; } + public PopOutSetting PopOutSetting { get; set; } = new(); - public RefocusSetting RefocusSetting { get; set; } + public RefocusSetting RefocusSetting { get; set; } = new(); - public TouchSetting TouchSetting { get; set; } + public TouchSetting TouchSetting { get; set; } = new(); - public TrackIRSetting TrackIRSetting { get; set; } + public TrackIRSetting TrackIRSetting { get; set; } = new(); - public WindowedModeSetting WindowedModeSetting { get; set; } + public WindowedModeSetting WindowedModeSetting { get; set; } = new(); - public SystemSetting SystemSetting { get; set; } + public SystemSetting SystemSetting { get; set; } = new(); - public KeyboardShortcutSetting KeyboardShortcutSetting { get; set; } + public KeyboardShortcutSetting KeyboardShortcutSetting { get; set; } = new(); - public event EventHandler IsUsedKeyboardShortcutChanged; + public DynamicLodSetting DynamicLodSetting { get; set; } = new(); + + public event EventHandler OnIsUsedKeyboardShortcutChanged; } } diff --git a/DomainModel/Setting/AutoPanning.cs b/DomainModel/Setting/AutoPanning.cs index 7c217e4..8fcc871 100644 --- a/DomainModel/Setting/AutoPanning.cs +++ b/DomainModel/Setting/AutoPanning.cs @@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class AutoPanning : ObservableObject { - public AutoPanning() - { - IsEnabled = true; - KeyBinding = "0"; - } + public bool IsEnabled { get; set; } = true; - public bool IsEnabled { get; set; } - - public string KeyBinding { get; set; } + public string KeyBinding { get; set; } = "0"; } } diff --git a/DomainModel/Setting/AutoPopOutSetting.cs b/DomainModel/Setting/AutoPopOutSetting.cs index 3877d89..682035d 100644 --- a/DomainModel/Setting/AutoPopOutSetting.cs +++ b/DomainModel/Setting/AutoPopOutSetting.cs @@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class AutoPopOutSetting : ObservableObject { - public AutoPopOutSetting() - { - IsEnabled = true; - ReadyToFlyDelay = 3; - } + public bool IsEnabled { get; set; } = true; - public bool IsEnabled { get; set; } - - public int ReadyToFlyDelay { get; set; } + public int ReadyToFlyDelay { get; set; } = 3; } } diff --git a/DomainModel/Setting/DynamicLodSetting.cs b/DomainModel/Setting/DynamicLodSetting.cs new file mode 100644 index 0000000..ad6847b --- /dev/null +++ b/DomainModel/Setting/DynamicLodSetting.cs @@ -0,0 +1,71 @@ +using MSFSPopoutPanelManager.DomainModel.DynamicLod; +using MSFSPopoutPanelManager.Shared; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Runtime.Serialization; + +namespace MSFSPopoutPanelManager.DomainModel.Setting +{ + public class DynamicLodSetting : ObservableObject + { + public DynamicLodSetting() + { + InitializeChildPropertyChangeBinding(); + + TlodConfigs.CollectionChanged += (_, e) => + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + if (e.NewItems?[0] is LodConfig item) + item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); + } + }; + + OlodConfigs.CollectionChanged += (_, e) => + { + if (e.Action == NotifyCollectionChangedAction.Add) + { + if (e.NewItems?[0] is LodConfig item) + item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); + } + }; + } + + public bool IsEnabled { get; set; } = false; + + public ObservableLodConfigLinkedList TlodConfigs { get; set; } = new(); + + public ObservableLodConfigLinkedList OlodConfigs { get; set; } = new(); + + public bool ResetEnabled { get; set; } = false; + + public int ResetTlod { get; set; } = 100; + + public int ResetOlod { get; set; } = 100; + + public void AddDefaultTLodConfigs() + { + TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 0, Lod = 100 })); + TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 5000, Lod = 200 })); + TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 10000, Lod = 300 })); + TlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 20000, Lod = 400 })); + } + + public void AddDefaultOLodConfigs() + { + OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 0, Lod = 200 })); + OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 1000, Lod = 150 })); + OlodConfigs.AddLast(new LinkedListNode(new LodConfig { Agl = 5000, Lod = 100 })); + } + + [OnDeserialized] + private void OnDeserialization(StreamingContext context) + { + foreach (var item in TlodConfigs) + item.PropertyChanged += (_, _) => TlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); + + foreach (var item in OlodConfigs) + item.PropertyChanged += (_, _) => OlodConfigs.OnNotifyCollectionChanged(NotifyCollectionChangedAction.Reset, item); + } + } +} diff --git a/DomainModel/Setting/GeneralSetting.cs b/DomainModel/Setting/GeneralSetting.cs index 803fdfc..7f7487d 100644 --- a/DomainModel/Setting/GeneralSetting.cs +++ b/DomainModel/Setting/GeneralSetting.cs @@ -7,35 +7,25 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public GeneralSetting() { - AlwaysOnTop = true; - MinimizeToTray = false; - StartMinimized = false; - AutoClose = true; - CheckForUpdate = true; - TurboMode = false; - InitializeChildPropertyChangeBinding(); } - public bool AlwaysOnTop { get; set; } + public bool AlwaysOnTop { get; set; } = true; - public bool AutoClose { get; set; } + public bool AutoClose { get; set; } = true; - public bool MinimizeToTray { get; set; } + public bool MinimizeToTray { get; set; } = false; - public bool StartMinimized { get; set; } + public bool StartMinimized { get; set; } = false; - public bool CheckForUpdate { get; set; } + public bool CheckForUpdate { get; set; } = true; - public bool TurboMode { get; set; } + public bool TurboMode { get; set; } = false; [JsonIgnore, IgnorePropertyChanged] public bool AutoStart { - get - { - return AppAutoStart.CheckIsAutoStart(); - } + get => AppAutoStart.CheckIsAutoStart(); set { if (value) diff --git a/DomainModel/Setting/KeyboardShortcutSetting.cs b/DomainModel/Setting/KeyboardShortcutSetting.cs index 583ce06..af74335 100644 --- a/DomainModel/Setting/KeyboardShortcutSetting.cs +++ b/DomainModel/Setting/KeyboardShortcutSetting.cs @@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class KeyboardShortcutSetting : ObservableObject { - public KeyboardShortcutSetting() - { - IsEnabled = true; - StartPopOutKeyBinding = "O"; - } + public bool IsEnabled { get; set; } = true; - public bool IsEnabled { get; set; } - - public string StartPopOutKeyBinding { get; set; } + public string StartPopOutKeyBinding { get; set; } = "O"; } } diff --git a/DomainModel/Setting/PopOutSetting.cs b/DomainModel/Setting/PopOutSetting.cs index 48d1d06..2d9215b 100644 --- a/DomainModel/Setting/PopOutSetting.cs +++ b/DomainModel/Setting/PopOutSetting.cs @@ -6,36 +6,25 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public PopOutSetting() { - MinimizeDuringPopOut = true; - MinimizeAfterPopOut = false; - UseLeftRightControlToPopOut = false; - AutoActivePause = false; - EnablePopOutMessages = true; - - AfterPopOutCameraView = new AfterPopOutCameraView(); - AutoPanning = new AutoPanning(); - PopOutTitleBarCustomization = new PopOutTitleBarCustomization(); - EnablePanelResetWhenLocked = true; - InitializeChildPropertyChangeBinding(); } - public AutoPanning AutoPanning { get; set; } + public AutoPanning AutoPanning { get; set; } = new(); - public AfterPopOutCameraView AfterPopOutCameraView { get; set; } + public AfterPopOutCameraView AfterPopOutCameraView { get; set; } = new(); - public bool MinimizeDuringPopOut { get; set; } + public bool MinimizeDuringPopOut { get; set; } = true; - public bool MinimizeAfterPopOut { get; set; } + public bool MinimizeAfterPopOut { get; set; } = false; - public bool UseLeftRightControlToPopOut { get; set; } + public bool UseLeftRightControlToPopOut { get; set; } = false; - public bool EnablePanelResetWhenLocked { get; set; } + public bool EnablePanelResetWhenLocked { get; set; } = true; - public bool EnablePopOutMessages { get; set; } + public bool EnablePopOutMessages { get; set; } = true; - public bool AutoActivePause { get; set; } + public bool AutoActivePause { get; set; } = false; - public PopOutTitleBarCustomization PopOutTitleBarCustomization { get; set; } + public PopOutTitleBarCustomization PopOutTitleBarCustomization { get; set; } = new(); }; } diff --git a/DomainModel/Setting/PopOutTitleBarCustomization.cs b/DomainModel/Setting/PopOutTitleBarCustomization.cs index 0d24775..7f9782c 100644 --- a/DomainModel/Setting/PopOutTitleBarCustomization.cs +++ b/DomainModel/Setting/PopOutTitleBarCustomization.cs @@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class PopOutTitleBarCustomization : ObservableObject { - public PopOutTitleBarCustomization() - { - IsEnabled = false; - HexColor = "000000"; - } + public bool IsEnabled { get; set; } = false; - public bool IsEnabled { get; set; } - - public string HexColor { get; set; } + public string HexColor { get; set; } = "000000"; } } diff --git a/DomainModel/Setting/RefocusGameWindow.cs b/DomainModel/Setting/RefocusGameWindow.cs index 45e7866..f63c59f 100644 --- a/DomainModel/Setting/RefocusGameWindow.cs +++ b/DomainModel/Setting/RefocusGameWindow.cs @@ -4,14 +4,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class RefocusGameWindow : ObservableObject { - public RefocusGameWindow() - { - IsEnabled = true; - Delay = 1; - } + public bool IsEnabled { get; set; } = true; - public bool IsEnabled { get; set; } - - public double Delay { get; set; } + public double Delay { get; set; } = 1; } } diff --git a/DomainModel/Setting/RefocusSetting.cs b/DomainModel/Setting/RefocusSetting.cs index 14cfaac..05a6adc 100644 --- a/DomainModel/Setting/RefocusSetting.cs +++ b/DomainModel/Setting/RefocusSetting.cs @@ -6,11 +6,9 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public RefocusSetting() { - RefocusGameWindow = new RefocusGameWindow(); - InitializeChildPropertyChangeBinding(); } - public RefocusGameWindow RefocusGameWindow { get; set; } + public RefocusGameWindow RefocusGameWindow { get; set; } = new(); } } diff --git a/DomainModel/Setting/SystemSetting.cs b/DomainModel/Setting/SystemSetting.cs index d24da04..d6af6cd 100644 --- a/DomainModel/Setting/SystemSetting.cs +++ b/DomainModel/Setting/SystemSetting.cs @@ -5,14 +5,8 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class SystemSetting : ObservableObject { - public SystemSetting() - { - LastUsedProfileId = Guid.Empty; - AutoUpdaterUrl = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml"; - } + public string AutoUpdaterUrl { get; set; } = "https://raw.githubusercontent.com/hawkeye-stan/msfs-popout-panel-manager/master/autoupdate.xml"; - public string AutoUpdaterUrl { get; set; } - - public Guid LastUsedProfileId { get; set; } + public Guid LastUsedProfileId { get; set; } = Guid.Empty; } } diff --git a/DomainModel/Setting/TouchSetting.cs b/DomainModel/Setting/TouchSetting.cs index fece8c1..7747361 100644 --- a/DomainModel/Setting/TouchSetting.cs +++ b/DomainModel/Setting/TouchSetting.cs @@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class TouchSetting : ObservableObject { - public TouchSetting() - { - TouchDownUpDelay = 0; - } - - public int TouchDownUpDelay { get; set; } + public int TouchDownUpDelay { get; set; } = 0; } } diff --git a/DomainModel/Setting/TrackIRSetting.cs b/DomainModel/Setting/TrackIRSetting.cs index 2dbf12f..33ecf64 100644 --- a/DomainModel/Setting/TrackIRSetting.cs +++ b/DomainModel/Setting/TrackIRSetting.cs @@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class TrackIRSetting : ObservableObject { - public TrackIRSetting() - { - AutoDisableTrackIR = true; - } - - public bool AutoDisableTrackIR { get; set; } + public bool AutoDisableTrackIR { get; set; } = false; } } diff --git a/DomainModel/Setting/WindowedModeSetting.cs b/DomainModel/Setting/WindowedModeSetting.cs index 7e931a6..f938066 100644 --- a/DomainModel/Setting/WindowedModeSetting.cs +++ b/DomainModel/Setting/WindowedModeSetting.cs @@ -4,11 +4,6 @@ namespace MSFSPopoutPanelManager.DomainModel.Setting { public class WindowedModeSetting : ObservableObject { - public WindowedModeSetting() - { - AutoResizeMsfsGameWindow = true; - } - - public bool AutoResizeMsfsGameWindow { get; set; } + public bool AutoResizeMsfsGameWindow { get; set; } = true; } } diff --git a/DomainModel/SimConnect/HudBarData737.cs b/DomainModel/SimConnect/HudBarData737.cs index 871ff21..670297e 100644 --- a/DomainModel/SimConnect/HudBarData737.cs +++ b/DomainModel/SimConnect/HudBarData737.cs @@ -1,7 +1,6 @@ using MSFSPopoutPanelManager.DomainModel.Profile; using MSFSPopoutPanelManager.Shared; using System; -using System.Reflection; namespace MSFSPopoutPanelManager.DomainModel.SimConnect { @@ -72,8 +71,9 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect private string MapGear(double gear) { - if (gear == 100) + if (Convert.ToInt32(gear) == 100) return "DOWN"; + if (gear == 0) return "UP"; @@ -82,12 +82,12 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect public void Clear() { - Type type = this.GetType(); - PropertyInfo[] properties = type.GetProperties(); - for (int i = 0; i < properties.Length; ++i) + var type = this.GetType(); + var properties = type.GetProperties(); + foreach (var property in properties) { - if (properties[i].SetMethod != null) - properties[i].SetValue(this, 0); + if (property.SetMethod != null) + property.SetValue(this, 0); } } } diff --git a/DomainModel/SimConnect/HudBarDataGeneric.cs b/DomainModel/SimConnect/HudBarDataGeneric.cs index c0efdbf..2875e78 100644 --- a/DomainModel/SimConnect/HudBarDataGeneric.cs +++ b/DomainModel/SimConnect/HudBarDataGeneric.cs @@ -1,7 +1,6 @@ using MSFSPopoutPanelManager.DomainModel.Profile; using MSFSPopoutPanelManager.Shared; using System; -using System.Reflection; namespace MSFSPopoutPanelManager.DomainModel.SimConnect { @@ -45,8 +44,9 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect private string MapGear(double gear) { - if (gear == 100) + if (Convert.ToInt32(gear) == 100) return "DOWN"; + if (gear == 0) return "UP"; @@ -55,12 +55,12 @@ namespace MSFSPopoutPanelManager.DomainModel.SimConnect public void Clear() { - Type type = this.GetType(); - PropertyInfo[] properties = type.GetProperties(); - for (int i = 0; i < properties.Length; ++i) + var type = this.GetType(); + var properties = type.GetProperties(); + foreach (var property in properties) { - if (properties[i].SetMethod != null) - properties[i].SetValue(this, 0); + if (property.SetMethod != null) + property.SetValue(this, 0); } } } diff --git a/MainApp/App.xaml.cs b/MainApp/App.xaml.cs index 8301a97..33832cc 100644 --- a/MainApp/App.xaml.cs +++ b/MainApp/App.xaml.cs @@ -1,5 +1,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using MSFSPopoutPanelManager.MainApp.AppWindow; using MSFSPopoutPanelManager.MainApp.ViewModel; using MSFSPopoutPanelManager.Orchestration; using MSFSPopoutPanelManager.Shared; @@ -9,21 +10,19 @@ using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; +using Application = System.Windows.Application; namespace MSFSPopoutPanelManager.MainApp { - public partial class App : Application + public partial class App { + public SharedStorage SharedStorage; + public static IHost AppHost { get; private set; } - - public App() - { - } - + protected override async void OnStartup(StartupEventArgs e) { DpiAwareness.Enable(); @@ -36,45 +35,60 @@ namespace MSFSPopoutPanelManager.MainApp } else { - // Setup all unhandle exception handlers + // Setup all unhandled exception handlers Dispatcher.UnhandledException += HandleDispatcherException; TaskScheduler.UnobservedTaskException += HandleTaskSchedulerUnobservedTaskException; AppDomain.CurrentDomain.UnhandledException += HandledDomainException; + // Setup all data storage objects + SharedStorage = new SharedStorage(); + SharedStorage.AppSettingData.ReadSettings(); + SharedStorage.ProfileData.ReadProfiles(); + // Setup dependency injections AppHost = Host.CreateDefaultBuilder() - .ConfigureServices((hostContext, services) => + .ConfigureServices((_, services) => { - services.AddSingleton(); + services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(s => new OrchestratorUIHelper(s.GetRequiredService())); + services.AddSingleton(s => new AppOrchestrator(SharedStorage, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + services.AddSingleton(s => new ProfileOrchestrator(SharedStorage)); + services.AddSingleton(s => new PanelSourceOrchestrator(SharedStorage, s.GetRequiredService())); + services.AddSingleton(s => new PanelPopOutOrchestrator(SharedStorage, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + services.AddSingleton(s => new PanelConfigurationOrchestrator(SharedStorage, s.GetRequiredService())); + services.AddSingleton(s => new FlightSimOrchestrator(SharedStorage)); + services.AddSingleton(s => new KeyboardOrchestrator(SharedStorage, s.GetRequiredService())); + services.AddSingleton(s => new HelpOrchestrator()); - services.AddSingleton(s => new ApplicationViewModel(s.GetRequiredService())); - services.AddSingleton(s => new HelpViewModel(s.GetRequiredService())); - services.AddSingleton(s => new ProfileCardListViewModel(s.GetRequiredService())); - services.AddSingleton(s => new ProfileCardViewModel(s.GetRequiredService())); - services.AddSingleton(s => new TrayIconViewModel(s.GetRequiredService())); + services.AddSingleton(s => new OrchestratorUiHelper(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddSingleton(s => new ApplicationViewModel(SharedStorage, s.GetRequiredService())); + services.AddSingleton(s => new HelpViewModel(SharedStorage, s.GetRequiredService())); + services.AddSingleton(s => new ProfileCardListViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddSingleton(s => new ProfileCardViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService(), s.GetRequiredService())); + services.AddSingleton(s => new TrayIconViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); - services.AddTransient(s => new AddProfileViewModel(s.GetRequiredService())); - services.AddTransient(s => new PopOutPanelListViewModel(s.GetRequiredService())); - services.AddTransient(s => new PopOutPanelCardViewModel(s.GetRequiredService())); - services.AddTransient(s => new PanelConfigFieldViewModel(s.GetRequiredService())); - services.AddTransient(s => new PanelCoorOverlayViewModel(s.GetRequiredService())); + services.AddTransient(s => new AddProfileViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddTransient(s => new PopOutPanelListViewModel(SharedStorage)); + services.AddTransient(s => new PopOutPanelConfigCardViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddTransient(s => new PopOutPanelSourceCardViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddTransient(s => new PopOutPanelSourceLegacyCardViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddTransient(s => new PanelConfigFieldViewModel(SharedStorage, s.GetRequiredService())); + services.AddTransient(s => new PanelCoorOverlayViewModel(SharedStorage)); - services.AddTransient(s => new MessageWindowViewModel(s.GetRequiredService())); - services.AddTransient(s => new HudBarViewModel(s.GetRequiredService())); + services.AddTransient(s => new MessageWindowViewModel(SharedStorage, s.GetRequiredService(), s.GetRequiredService())); + services.AddTransient(s => new HudBarViewModel(SharedStorage, s.GetRequiredService())); + services.AddTransient(s => new NumPadViewModel(SharedStorage)); }).Build(); await AppHost!.StartAsync(); // Startup window (must come after DPI setup above) - MainWindow = AppHost.Services.GetRequiredService(); - MainWindow.Show(); + MainWindow = AppHost.Services.GetRequiredService(); + MainWindow?.Show(); // Setup orchestration UI handler - var orchestrationUIHelper = App.AppHost.Services.GetRequiredService(); + App.AppHost.Services.GetRequiredService(); // Setup message window dialog var messageWindow = new MessageWindow(); @@ -86,10 +100,19 @@ namespace MSFSPopoutPanelManager.MainApp private bool IsRunning() { - return Process.GetProcesses().Count(p => p.ProcessName.Contains(Assembly.GetEntryAssembly().GetName().Name)) > 1; + var assembly = Assembly.GetEntryAssembly(); + + if (assembly == null) + return false; + + var assemblyName = assembly.GetName().Name; + if (string.IsNullOrEmpty(assemblyName)) + return false; + + return Process.GetProcesses().Count(p => p.ProcessName.Contains(assemblyName)) > 1; } - private void HandleTaskSchedulerUnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) + private void HandleTaskSchedulerUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { FileLogger.WriteException(e.Exception.Message, e.Exception); } @@ -110,18 +133,18 @@ namespace MSFSPopoutPanelManager.MainApp } } - public enum PROCESS_DPI_AWARENESS + public enum ProcessDpiAwareness { - Process_DPI_Unaware = 0, - Process_System_DPI_Aware = 1, - Process_Per_Monitor_DPI_Aware = 2 + //PROCESS_DPI_UNAWARE = 0, + //PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 } - public enum DPI_AWARENESS_CONTEXT + public enum DpiAwarenessContext { - DPI_AWARENESS_CONTEXT_UNAWARE = 16, - DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17, - DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18, + //DPI_AWARENESS_CONTEXT_UNAWARE = 16, + //DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = 17, + //DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = 18, DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = 34 } @@ -135,11 +158,11 @@ namespace MSFSPopoutPanelManager.MainApp // Windows 10 creators update added support for per monitor v2 if (Environment.OSVersion.Version >= new Version(10, 0, 15063)) { - SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); + SetProcessDpiAwarenessContext(DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); } else { - SetProcessDpiAwareness(PROCESS_DPI_AWARENESS.Process_Per_Monitor_DPI_Aware); + SetProcessDpiAwareness(ProcessDpiAwareness.PROCESS_PER_MONITOR_DPI_AWARE); } } else @@ -148,20 +171,19 @@ namespace MSFSPopoutPanelManager.MainApp } var process = WindowProcessManager.GetApplicationProcess(); - PROCESS_DPI_AWARENESS outValue; - GetProcessDpiAwareness(process.Handle, out outValue); + GetProcessDpiAwareness(process.Handle, out _); } [DllImport("User32.dll")] - internal static extern bool SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT dpiFlag); + internal static extern bool SetProcessDpiAwarenessContext(DpiAwarenessContext dpiFlag); [DllImport("SHCore.dll")] - internal static extern bool SetProcessDpiAwareness(PROCESS_DPI_AWARENESS awareness); + internal static extern bool SetProcessDpiAwareness(ProcessDpiAwareness awareness); [DllImport("User32.dll")] internal static extern bool SetProcessDPIAware(); [DllImport("SHCore.dll", SetLastError = true)] - internal static extern void GetProcessDpiAwareness(IntPtr hprocess, out PROCESS_DPI_AWARENESS awareness); + internal static extern void GetProcessDpiAwareness(IntPtr hProcess, out ProcessDpiAwareness awareness); } } \ No newline at end of file diff --git a/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml new file mode 100644 index 0000000..8c7237c --- /dev/null +++ b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml.cs b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml.cs new file mode 100644 index 0000000..b3c39b3 --- /dev/null +++ b/MainApp/AppUserControl/Dialog/AddProfileDialog.xaml.cs @@ -0,0 +1,29 @@ +using MaterialDesignThemes.Wpf; +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using System.ComponentModel; +using System.Windows; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog +{ + public partial class AddProfileDialog + { + public AddProfileViewModel ViewModel { get; set; } + + public AddProfileDialog() + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + ViewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => + { + DataContext = ViewModel; + BtnAccept.IsEnabled = false; + }; + } + + public DialogClosingEventHandler ClosingEventHandler => ViewModel.ClosingEventHandler; + } +} diff --git a/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml b/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml new file mode 100644 index 0000000..22378c5 --- /dev/null +++ b/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml.cs b/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml.cs new file mode 100644 index 0000000..40c5898 --- /dev/null +++ b/MainApp/AppUserControl/Dialog/ConfirmationDialog.xaml.cs @@ -0,0 +1,18 @@ +using MSFSPopoutPanelManager.MainApp.ViewModel; +using System.ComponentModel; +using System.Windows; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.Dialog +{ + public partial class ConfirmationDialog + { + public ConfirmationDialog(string content, string confirmButtonText) + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + DataContext = new ConfirmationViewModel(content, confirmButtonText); + } + } +} diff --git a/MainApp/AppUserControl/DynamicLodPreference.xaml b/MainApp/AppUserControl/DynamicLodPreference.xaml new file mode 100644 index 0000000..b985f66 --- /dev/null +++ b/MainApp/AppUserControl/DynamicLodPreference.xaml @@ -0,0 +1,556 @@ + + + 22 + 22 + + + + + + + + + Terrain Level of Detail (TLOD) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Object Level of Detail (OLOD) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Enable reset of TLOD and OLOD to the following values when flight session ends. + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/DynamicLodPreference.xaml.cs b/MainApp/AppUserControl/DynamicLodPreference.xaml.cs new file mode 100644 index 0000000..d8c07c1 --- /dev/null +++ b/MainApp/AppUserControl/DynamicLodPreference.xaml.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using MSFSPopoutPanelManager.DomainModel.DynamicLod; +using MSFSPopoutPanelManager.MainApp.ViewModel; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class DynamicLodPreference + { + private ObservableLodConfigLinkedList _tlodConfigs; + private ObservableLodConfigLinkedList _olodConfigs; + + public DynamicLodPreference() + { + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + { + InitializeComponent(); + return; + } + + AddTlodConfigs = new ObservableCollection() { new () }; + AddOlodConfigs = new ObservableCollection() { new () }; + + Loaded += (_, _) => + { + InitializeComponent(); + var dataContext = DataContext as ApplicationViewModel; + _tlodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.TlodConfigs; + _olodConfigs = dataContext?.AppSettingData.ApplicationSetting.DynamicLodSetting.OlodConfigs; + }; + } + + public ObservableCollection AddTlodConfigs { get; set; } + + public ObservableCollection AddOlodConfigs { get; set; } + + private void AddTlod_SourceUpdated(object sender, DataTransferEventArgs e) + { + var textBox = sender as TextBox; + + var lodConfig = textBox?.DataContext as TempLodConfig; + + if (lodConfig?.Agl == null || lodConfig.Lod == null) + return; + + if (UpdateTlodDuplicate(lodConfig)) + { + RebindTLodGrid(); + return; + } + + var targetLodConfig = _tlodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl); + var newLodConfig = new LodConfig { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod }; + + if (targetLodConfig == null) + _tlodConfigs.AddFirst(newLodConfig); + else + _tlodConfigs.AddAfter(_tlodConfigs.Find(targetLodConfig), new LinkedListNode(newLodConfig)); + + RebindTLodGrid(); + } + + private void TLodDelete_Click(object sender, RoutedEventArgs e) + { + var button = e.Source as Button; + + if(button?.DataContext is not LodConfig lodConfig) + return; + + _tlodConfigs.Remove(lodConfig); + + RebindTLodGrid(); + } + + private void RebindTLodGrid() + { + this.TlodGrid.ItemsSource = null; + this.TlodGrid.ItemsSource = _tlodConfigs.ToList(); + + AddTlodConfigs.Clear(); + AddTlodConfigs.Add(new TempLodConfig()); + } + + private bool UpdateTlodDuplicate(TempLodConfig lodConfig) + { + var tlodConfig = _tlodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl); + + if(tlodConfig == null) + return false; + + tlodConfig.Lod = Convert.ToInt32(lodConfig.Lod); + return true; + } + + private void AddOlod_SourceUpdated(object sender, DataTransferEventArgs e) + { + var textBox = sender as TextBox; + + var lodConfig = textBox?.DataContext as TempLodConfig; + + if (lodConfig?.Agl == null || lodConfig.Lod == null) + return; + + if (UpdateOlodDuplicate(lodConfig)) + { + RebindOLodGrid(); + return; + } + + var targetLodConfig = _olodConfigs.LastOrDefault(x => lodConfig.Agl >= x.Agl); + var newLodConfig = new LodConfig() { Agl = (int)lodConfig.Agl, Lod = (int)lodConfig.Lod }; + + if (targetLodConfig == null) + _olodConfigs.AddFirst(newLodConfig); + else + _olodConfigs.AddAfter(_olodConfigs.Find(targetLodConfig), new LinkedListNode(newLodConfig)); + + RebindOLodGrid(); + } + + private void OLodDelete_Click(object sender, RoutedEventArgs e) + { + var button = e.Source as Button; + + if (button?.DataContext is not LodConfig lodConfig) + return; + + _olodConfigs.Remove(lodConfig); + + RebindOLodGrid(); + } + + private void RebindOLodGrid() + { + this.OlodGrid.ItemsSource = null; + this.OlodGrid.ItemsSource = _olodConfigs.ToList(); + + AddOlodConfigs.Clear(); + AddOlodConfigs.Add(new TempLodConfig()); + } + + private bool UpdateOlodDuplicate(TempLodConfig lodConfig) + { + var olodConfig = _olodConfigs.FirstOrDefault(x => x.Agl == lodConfig.Agl); + + if (olodConfig == null) + return false; + + olodConfig.Lod = Convert.ToInt32(lodConfig.Lod); + return true; + } + } + + public class TempLodConfig + { + public int? Agl { get; set; } + + public int? Lod { get; set; } + } + + public class AglValidationRule : ValidationRule + { + private const int MIN = -2000; + private const int MAX = 99999; + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + var agl = 0; + + try + { + var valueString = value as string; + if (valueString?.Length > 0) + agl = int.Parse(valueString); + } + catch + { + return new ValidationResult(false, "AGL must be a number."); + } + + if (agl is < MIN or > MAX) + { + return new ValidationResult(false, + $"AGL must be in the range: {MIN} - {MAX}."); + } + return ValidationResult.ValidResult; + } + } + + public class LodValidationRule : ValidationRule + { + private const int MIN = 0; + private const int MAX = 400; + + public override ValidationResult Validate(object value, CultureInfo cultureInfo) + { + var lod = 0; + + try + { + var valueString = value as string; + if (valueString?.Length > 0) + lod = int.Parse(valueString); + } + catch + { + return new ValidationResult(false, "LOD must be a number."); + } + + if (lod is < MIN or > MAX) + { + return new ValidationResult(false, + $"LOD must be in the range: {MIN} - {MAX}."); + } + return ValidationResult.ValidResult; + } + } +} diff --git a/MainApp/AppUserControl/HelpDrawer.xaml b/MainApp/AppUserControl/HelpDrawer.xaml new file mode 100644 index 0000000..0c81733 --- /dev/null +++ b/MainApp/AppUserControl/HelpDrawer.xaml @@ -0,0 +1,334 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Keyboard Commands + + + + To configure a pop out panel, first click on the move and resize icon + + + for the panel. You can then use keyboard commands below to adjust pop out panel when the icon turns green. To end panel configuration using keyboard commands, just click the icon + + + again to end panel adjustment. + + Up Arrow + - Move panel up by 10 pixels + Down Arrow + - Move panel down by 10 pixels + Left Arrow + - Move panel left by 10 pixels + Right Arrow + - Move panel right by 10 pixels + + Shift + Up Arrow + - Move panel up by 1 pixel + Shift + Down Arrow + - Move panel down by 1 pixel + Shift + Left Arrow + - Move panel left by 1 pixel + Shift + Right Arrow + - Move panel right by 1 pixel + + Ctrl + Up Arrow + - Decrease height by 10 pixels + Ctrl + Down Arrow + - Increase height by 10 pixels + Ctrl + Left Arrow + - Decrease width by 10 pixels + Ctrl + Right Arrow + - Increase width by 10 pixels + + Shift + Ctrl + Up Arrow + - Decrease height by 1 pixel + Shift + Ctrl + Down Arrow + - Increase height by 1 pixel + Shift + Ctrl + Left Arrow + - Decrease width by 1 pixel + Shift + Ctrl + Right Arrow + - Increase width by 1 pixel + + + + + + + + + User Guide + + + + + + + + + + + + + + + + + + Download Latest Release + + + + + + + + + + + + + + + + + + Support + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelCard/MoveUpDownButton.xaml.cs b/MainApp/AppUserControl/PopOutPanelCard/MoveUpDownButton.xaml.cs new file mode 100644 index 0000000..5031c05 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/MoveUpDownButton.xaml.cs @@ -0,0 +1,10 @@ +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard +{ + public partial class MoveUpDownButton + { + public MoveUpDownButton() + { + InitializeComponent(); + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml b/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml new file mode 100644 index 0000000..8032f38 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml @@ -0,0 +1,145 @@ + + + 14 + 22 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml.cs b/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml.cs new file mode 100644 index 0000000..68c4d11 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/PanelConfigField.xaml.cs @@ -0,0 +1,112 @@ +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.DomainModel.Profile; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Input; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard +{ + public partial class PanelConfigField + { + private readonly PanelConfigFieldViewModel _viewModel; + + public static readonly DependencyProperty DataItemProperty = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PanelConfigField)); + public static readonly DependencyProperty BindingPathProperty = DependencyProperty.Register(nameof(BindingPath), typeof(string), typeof(PanelConfigField)); + public static readonly RoutedEvent SourceUpdatedEvent = EventManager.RegisterRoutedEvent("SourceUpdatedEvent", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(PanelConfigField)); + + public PanelConfigField() + { + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + { + InitializeComponent(); + return; + } + + _viewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => + { + _viewModel.DataItem = DataItem; + _viewModel.BindingPath = BindingPath; + _viewModel.SourceUpdatedEvent = SourceUpdatedEvent; + this.DataContext = _viewModel; + InitializeComponent(); + + var binding = new Binding($"DataItem.{BindingPath}") + { + Mode = BindingMode.TwoWay, + NotifyOnSourceUpdated = true + }; + + TxtBoxData?.SetBinding(TextBox.TextProperty, binding); + }; + } + + public PanelConfig DataItem + { + get => (PanelConfig)GetValue(DataItemProperty); + set => SetValue(DataItemProperty, value); + } + + public string BindingPath + { + get => (string)GetValue(BindingPathProperty); + set => SetValue(BindingPathProperty, value); + } + + private void TextBox_KeyDown(object sender, KeyEventArgs e) + { + if (e.Key != Key.Enter) + return; + + Keyboard.ClearFocus(); + FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this.Parent), this.Parent as IInputElement); + } + + private void TextBox_GotFocus(object sender, RoutedEventArgs e) + { + TxtBoxData.Dispatcher.BeginInvoke(new Action(() => TxtBoxData.SelectAll())); + } + + private string _oldText; + + private void TxtBox_NumbersOnly(object sender, TextCompositionEventArgs e) + { + e.Handled = !(int.TryParse(e.Text, out _) || (e.Text.Trim() == "-")); + + if (!e.Handled) + _oldText = ((TextBox)sender).Text; + } + + private void TxtBox_NumbersOnlyTextChanged(object sender, TextChangedEventArgs e) + { + var txtBox = (TextBox)sender; + + if (String.IsNullOrEmpty(txtBox.Text)) + txtBox.Text = "0"; + + else if (!(int.TryParse(txtBox.Text, out _) || (txtBox.Text.Trim() == "-"))) + { + txtBox.Text = _oldText; + } + } + + private void Data_SourceUpdated(object sender, DataTransferEventArgs e) + { + _viewModel.DataUpdatedCommand.Execute(null); + } + + private void BtnPopupBoxOpen_Click(object sender, RoutedEventArgs e) + { + PopupBoxAdjustment.IsPopupOpen = true; + } + + private void BtnPopupBoxClose_Click(object sender, RoutedEventArgs e) + { + PopupBoxAdjustment.IsPopupOpen = false; + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml b/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml new file mode 100644 index 0000000..94d822a --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml @@ -0,0 +1,84 @@ + + + 18 + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml.cs b/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml.cs new file mode 100644 index 0000000..29056be --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/PanelTargetField.xaml.cs @@ -0,0 +1,108 @@ +using MSFSPopoutPanelManager.DomainModel.Profile; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using System.Linq; +using System.Windows; +using System.Windows.Controls; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard +{ + /// + /// Interaction logic for PanelTargetField.xaml + /// + public partial class PanelTargetField + { + public PanelTargetField() + { + InitializeComponent(); + this.Loaded += PanelTargetField_Loaded; + } + + private void PanelTargetField_Loaded(object sender, RoutedEventArgs e) + { + var dataContext = ((PopOutPanelSourceCardViewModel) this.DataContext); + + if (dataContext == null) + return; + + dataContext.FixedCameraConfigs.CollectionChanged += (_, _) => + { + var items = dataContext.FixedCameraConfigs; + + this.ComboBoxCameraSelection.ItemsSource = items; + this.ComboBoxCameraSelection.DisplayMemberPath = "Name"; + + var index = items.ToList().FindIndex(x => x.Id == dataContext.DataItem.FixedCameraConfig.Id); + + if (index == -1) + return; + + this.ComboBoxCameraSelection.SelectedIndex = index; + }; + } + + private void PopupBoxCameraSelectionPrev_Clicked(object sender, RoutedEventArgs e) + { + var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext); + var selectedItem = dataContext.DataItem.FixedCameraConfig; + + // rebinding the camera list with erase the selected item, need the next line to set the selectedItem + var items = dataContext.FixedCameraConfigs; + + if (selectedItem == null) + return; + + var index = items.ToList().FindIndex(x => x.Name == selectedItem.Name); + + if (index == -1) + return; + + if (index == 0) + index = items.Count - 1; + else + index -= 1; + + this.ComboBoxCameraSelection.SelectedIndex = index; + dataContext.DataItem.FixedCameraConfig = items[index]; // assign and save item + + //dataContext.SetCamera(); + } + + private void PopupBoxCameraSelectionNext_Clicked(object sender, RoutedEventArgs e) + { + var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext); + var selectedItem = dataContext.DataItem.FixedCameraConfig; + + // rebinding the camera list with erase the selected item, need the next line to set the selectedItem + var items = dataContext.FixedCameraConfigs; + + if (selectedItem == null) + return; + + var index = items.ToList().FindIndex(x => x.Name == selectedItem.Name); + + if (index == -1) + index = 0; + + if (index == items.Count - 1) + index = 0; + else + index += 1; + + this.ComboBoxCameraSelection.SelectedIndex = index; + dataContext.DataItem.FixedCameraConfig = items[index]; // assign and save item + + //dataContext.SetCamera(); + } + + private void ComboBoxCameraSelection_OnSelectionChanged(object sender, SelectionChangedEventArgs e) + { + var dataContext = ((PopOutPanelSourceCardViewModel)this.DataContext); + + if (e.AddedItems.Count <= 0) + return; + + dataContext.DataItem.FixedCameraConfig = (FixedCameraConfig)e.AddedItems[0]; + dataContext.SetCamera(); + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml b/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml new file mode 100644 index 0000000..3aad002 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml @@ -0,0 +1,40 @@ + + + 22 + 28 + + + + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml.cs b/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml.cs new file mode 100644 index 0000000..3de2de0 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelCard/TouchEnabledButton.xaml.cs @@ -0,0 +1,10 @@ +namespace MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard +{ + public partial class TouchEnabledButton + { + public TouchEnabledButton() + { + InitializeComponent(); + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelConfigCard.xaml b/MainApp/AppUserControl/PopOutPanelConfigCard.xaml new file mode 100644 index 0000000..2249aab --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelConfigCard.xaml @@ -0,0 +1,337 @@ + + + 22 + 28 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Always on Top + + + + + + Full Screen Mode + + + + + + Hide Title Bar + + + + + + Automatic Game Refocus + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelConfigCard.xaml.cs b/MainApp/AppUserControl/PopOutPanelConfigCard.xaml.cs new file mode 100644 index 0000000..0f6d281 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelConfigCard.xaml.cs @@ -0,0 +1,71 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Input; +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.DomainModel.Profile; +using MSFSPopoutPanelManager.MainApp.AppUserControl.PopOutPanelCard; +using MSFSPopoutPanelManager.MainApp.ViewModel; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class PopOutPanelConfigCard + { + private readonly PopOutPanelConfigCardViewModel _viewModel; + public static readonly DependencyProperty DataItemProperty2 = DependencyProperty.Register(nameof(DataItem), typeof(PanelConfig), typeof(PopOutPanelConfigCard)); + + public PopOutPanelConfigCard() + { + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + { + InitializeComponent(); + return; + } + + _viewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => + { + _viewModel.DataItem = DataItem; + DataContext = _viewModel; + InitializeComponent(); + }; + } + + public PanelConfig DataItem + { + get => (PanelConfig)GetValue(DataItemProperty2); + set => SetValue(DataItemProperty2, value); + } + + private void TextBox_KeyDown(object sender, KeyEventArgs e) + { + if (e is not { Key: Key.Enter }) + return; + + Keyboard.ClearFocus(); + FocusManager.SetFocusedElement(FocusManager.GetFocusScope(RootExpander), RootExpander); + } + + private void TextBox_GotFocus(object sender, RoutedEventArgs e) + { + var txtBox = (TextBox)sender; + txtBox.Dispatcher.BeginInvoke(new Action(() => txtBox.SelectAll())); + } + + private void Data_SourceUpdated(object sender, System.Windows.Data.DataTransferEventArgs e) + { + var param = sender switch + { + PanelConfigField field => field.BindingPath, + ToggleButton button => button.Name.Substring(6), + TextBox box => box.Name.Substring(6), + _ => null + }; + + if (!string.IsNullOrEmpty(param)) + _viewModel.PanelAttributeUpdatedCommand.Execute(param); + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelList.xaml b/MainApp/AppUserControl/PopOutPanelList.xaml new file mode 100644 index 0000000..c568354 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelList.xaml @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/PopOutPanelList.xaml.cs b/MainApp/AppUserControl/PopOutPanelList.xaml.cs new file mode 100644 index 0000000..17a7369 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelList.xaml.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class PopOutPanelList + { + public PopOutPanelList() + { + InitializeComponent(); + } + } + + public class DummyConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value; + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelListEmpty.xaml b/MainApp/AppUserControl/PopOutPanelListEmpty.xaml new file mode 100644 index 0000000..6ce5c79 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelListEmpty.xaml @@ -0,0 +1,46 @@ + + + + + + Click here to add + + + + Add pop out panels to aircraft profile + + + Identifying instrumentation source panel location in the game. + You must be in cockpit view to start this process. + + diff --git a/MainApp/AppUserControl/PopOutPanelListEmpty.xaml.cs b/MainApp/AppUserControl/PopOutPanelListEmpty.xaml.cs new file mode 100644 index 0000000..407284e --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelListEmpty.xaml.cs @@ -0,0 +1,10 @@ +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class PopOutPanelListEmpty + { + public PopOutPanelListEmpty() + { + InitializeComponent(); + } + } +} diff --git a/MainApp/AppUserControl/PopOutPanelSourceCard.xaml b/MainApp/AppUserControl/PopOutPanelSourceCard.xaml new file mode 100644 index 0000000..53d6e17 --- /dev/null +++ b/MainApp/AppUserControl/PopOutPanelSourceCard.xaml @@ -0,0 +1,138 @@ + + + + 22 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Active Aircraft: + + + + + + + + + + + + + + + + + + + + + + + + Power on is required to pop out panels on cold start (for G1000 based aircraft ONLY if needed) + + + + + Include in-game menu bar panels for pop out management and touch screen support + + + + + Add a virtual keyboard NumPad + + + + + + Add a HUD Bar + + + + + + + + Enable entire monitor display to have game refocus function when touch + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/ProfileCardList.xaml.cs b/MainApp/AppUserControl/ProfileCardList.xaml.cs new file mode 100644 index 0000000..c2e0de2 --- /dev/null +++ b/MainApp/AppUserControl/ProfileCardList.xaml.cs @@ -0,0 +1,42 @@ +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using System.ComponentModel; +using System.Windows; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class ProfileCardList + { + private readonly ProfileCardListViewModel _viewModel; + + public ProfileCardList() + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + _viewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => + { + DataContext = _viewModel; + _viewModel.OnProfileSelected += (_, _) => + { + PopupBoxFinder.StaysOpen = false; + PopupBoxFinder.IsPopupOpen = false; + }; + }; + } + + private void BtnPopupBoxFinder_Click(object sender, RoutedEventArgs e) + { + PopupBoxFinder.IsPopupOpen = !PopupBoxFinder.IsPopupOpen; + PopupBoxFinder.StaysOpen = PopupBoxFinder.IsPopupOpen; + + if (PopupBoxFinder.IsPopupOpen) + { + ComboBoxSearchProfile.Text = null; + ComboBoxSearchProfile.Focus(); + } + } + } +} diff --git a/MainApp/AppUserControl/TrayIcon.xaml b/MainApp/AppUserControl/TrayIcon.xaml new file mode 100644 index 0000000..bfcb316 --- /dev/null +++ b/MainApp/AppUserControl/TrayIcon.xaml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppUserControl/TrayIcon.xaml.cs b/MainApp/AppUserControl/TrayIcon.xaml.cs new file mode 100644 index 0000000..1b795aa --- /dev/null +++ b/MainApp/AppUserControl/TrayIcon.xaml.cs @@ -0,0 +1,35 @@ +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using Prism.Commands; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; + +namespace MSFSPopoutPanelManager.MainApp.AppUserControl +{ + public partial class TrayIcon + { + // This command has to be here since it doesn't work in view model, window StateChanged never gets fire + public DelegateCommand RestoreWindowCommand => new(() => { ((Window)((Border)((Grid)this.Parent).Parent).Parent).WindowState = WindowState.Normal; }, () => true); + + private TrayIconViewModel ViewModel { get; } + + public TrayIcon() + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + ViewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => { DataContext = ViewModel; }; + + Tray.DoubleClickCommand = RestoreWindowCommand; + } + + private void MenuItem_Click(object sender, RoutedEventArgs e) + { + if (Tray.ContextMenu != null) + Tray.ContextMenu.IsOpen = true; + } + } +} diff --git a/MainApp/AppWindow/AppMainWindow.xaml b/MainApp/AppWindow/AppMainWindow.xaml new file mode 100644 index 0000000..c85b5fc --- /dev/null +++ b/MainApp/AppWindow/AppMainWindow.xaml @@ -0,0 +1,165 @@ + + + 28 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MainApp/AppWindow/AppMainWindow.xaml.cs b/MainApp/AppWindow/AppMainWindow.xaml.cs new file mode 100644 index 0000000..d916059 --- /dev/null +++ b/MainApp/AppWindow/AppMainWindow.xaml.cs @@ -0,0 +1,102 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Interop; +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.DomainModel.Profile; +using MSFSPopoutPanelManager.MainApp.AppUserControl; +using MSFSPopoutPanelManager.MainApp.ViewModel; +using MSFSPopoutPanelManager.WindowsAgent; + +namespace MSFSPopoutPanelManager.MainApp.AppWindow +{ + public partial class AppMainWindow + { + private readonly ApplicationViewModel _viewModel; + + public AppMainWindow() + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + _viewModel = App.AppHost.Services.GetRequiredService(); + Loaded += AppWindow_Loaded; + Closing += AppWindow_Closing; + StateChanged += AppWindow_StateChanged; + WindowActionManager.OnPopOutManagerAlwaysOnTopChanged += (_, e) => { Topmost = e; }; + MouseLeftButtonDown += (_, _) => DragMove(); + } + + private void AppWindow_Loaded(object sender, RoutedEventArgs e) + { + var window = Window.GetWindow(this); + + if (window == null) + throw new ApplicationException("Cannot instantiate Pop Out Panel Manager."); + + _viewModel.ApplicationHandle = new WindowInteropHelper(window).Handle; + _viewModel.ApplicationWindow = Application.Current.MainWindow; + _viewModel.Initialize(); + + DataContext = _viewModel; + + // Try to fix always on to click through. This won't work for application's title bar since mouseEnter won't be triggered. + // This is super tricky to trick POPM process is MSFS process to avoid Windows OS focus stealing + MouseEnter += (_, _) =>WindowActionManager.SetWindowFocus(WindowProcessManager.GetApplicationProcess().Handle); + } + + private void AppWindow_Closing(object sender, CancelEventArgs e) + { + e.Cancel = true; + _viewModel.WindowClosing(); + } + + private void AppWindow_StateChanged(object sender, EventArgs e) + { + switch (WindowState) + { + case WindowState.Maximized: + ShowInTaskbar = true; + break; + case WindowState.Minimized: + if (_viewModel.AppSettingData.ApplicationSetting.GeneralSetting.MinimizeToTray) + { + SystemTrayIcon.Tray.Visibility = Visibility.Visible; + ShowInTaskbar = false; + } + break; + case WindowState.Normal: + SystemTrayIcon.Tray.Visibility = Visibility.Hidden; + ShowInTaskbar = true; + + // Fix always on top status once app is minimize and then restore + if (_viewModel.AppSettingData.ApplicationSetting.GeneralSetting.AlwaysOnTop) + WindowActionManager.ApplyAlwaysOnTop(_viewModel.ApplicationHandle, PanelType.PopOutManager, true); + break; + } + } + + private void SettingsButton_Click(object sender, RoutedEventArgs e) + { + PanelDrawers.Children.Clear(); + PanelDrawers.Children.Add(new PreferenceDrawer()); + } + + private void HelpButton_Click(object sender, RoutedEventArgs e) + { + PanelDrawers.Children.Clear(); + PanelDrawers.Children.Add(new HelpDrawer()); + } + + private void BtnMinimize_Click(object sender, RoutedEventArgs e) + { + WindowState = WindowState.Minimized; + } + + private void BtnClose_Click(object sender, RoutedEventArgs e) + { + Close(); + } + } +} diff --git a/MainApp/AppWindow/HudBar.xaml b/MainApp/AppWindow/HudBar.xaml new file mode 100644 index 0000000..86a1e39 --- /dev/null +++ b/MainApp/AppWindow/HudBar.xaml @@ -0,0 +1,284 @@ + + + 22 + 28 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Elevator Trim: + + + + + + + Aileron Trim: + + + + + + + Rudder Trim: + + + + + + + Flaps: + + + + + + + Brake: + + + + + + + + + + + Gear: + + + + + + + + + + + + + + + + + + + + + + + Timer: + + + + + + + + + + + Sim Rate: + + + + + + + + + + + + + + + + diff --git a/MainApp/AppWindow/HudBar.xaml.cs b/MainApp/AppWindow/HudBar.xaml.cs new file mode 100644 index 0000000..7833a6f --- /dev/null +++ b/MainApp/AppWindow/HudBar.xaml.cs @@ -0,0 +1,57 @@ +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; +using System.Windows.Interop; +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.MainApp.ViewModel; + +namespace MSFSPopoutPanelManager.MainApp.AppWindow +{ + public partial class HudBar + { + private readonly HudBarViewModel _viewModel; + + public HudBar(Guid panelId) + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + _viewModel = App.AppHost.Services.GetRequiredService(); + _viewModel.PanelId = panelId; + Loaded += (_, _) => + { + DataContext = _viewModel; + + var window = Window.GetWindow(this); + if (window == null) + throw new ApplicationException("Unable to instantiate HudBar window"); + + _viewModel.PanelConfig.PanelHandle = new WindowInteropHelper(window).Handle; + _viewModel.PanelConfig.Width = Convert.ToInt32(Width); + _viewModel.PanelConfig.Height = Convert.ToInt32(Height); + }; + + this.MouseLeftButtonDown += HudBar_MouseLeftButtonDown; + this.Topmost = true; + + Closing += HudBar_Closing; + } + + private void HudBar_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + _viewModel.CloseCommand.Execute(null); + } + + private void HudBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + DragMove(); + } + + private void BtnClose_Click(object sender, RoutedEventArgs e) + { + this.Close(); + } + } +} diff --git a/MainApp/AppWindow/MessageWindow.xaml b/MainApp/AppWindow/MessageWindow.xaml new file mode 100644 index 0000000..82a5572 --- /dev/null +++ b/MainApp/AppWindow/MessageWindow.xaml @@ -0,0 +1,53 @@ + + + + + + MSFS POP OUT PANEL MANAGER + + + + + + + + + + diff --git a/MainApp/AppWindow/MessageWindow.xaml.cs b/MainApp/AppWindow/MessageWindow.xaml.cs new file mode 100644 index 0000000..5acd348 --- /dev/null +++ b/MainApp/AppWindow/MessageWindow.xaml.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Interop; +using Microsoft.Extensions.DependencyInjection; +using MSFSPopoutPanelManager.MainApp.ViewModel; + +namespace MSFSPopoutPanelManager.MainApp.AppWindow +{ + public partial class MessageWindow + { + private readonly MessageWindowViewModel _viewModel; + + public MessageWindow() + { + InitializeComponent(); + if (DesignerProperties.GetIsInDesignMode(new DependencyObject())) + return; + + _viewModel = App.AppHost.Services.GetRequiredService(); + Loaded += (_, _) => + { + DataContext = _viewModel; + + var window = Window.GetWindow(this); + if (window == null) + throw new ApplicationException("Unable to instantiate status message window"); + + _viewModel.Handle = new WindowInteropHelper(window).Handle; + + // Set window binding, needs to be in code after window loaded + var visibleBinding = new Binding("IsVisible") + { + Source = _viewModel, + Converter = new BooleanToVisibilityConverter() + }; + BindingOperations.SetBinding(this, Window.VisibilityProperty, visibleBinding); + + // Set window click through + WindowsServices.SetWindowExTransparent(_viewModel.Handle); + + _viewModel.OnMessageUpdated += ViewModel_OnMessageUpdated; + }; + } + + private void ViewModel_OnMessageUpdated(object sender, List e) + { + if (e == null) + return; + + TextBlockMessage.Inlines.Clear(); + + foreach (var run in e) + TextBlockMessage.Inlines.Add(run); + + ScrollViewerMessage.ScrollToEnd(); + } + } + + public static class WindowsServices + { + const int WS_EX_TRANSPARENT = 0x00000020; + const int GWL_EXSTYLE = (-20); + + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int index); + + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int index, int newStyle); + + public static void SetWindowExTransparent(IntPtr hWnd) + { + var extendedStyle = GetWindowLong(hWnd, GWL_EXSTYLE); + SetWindowLong(hWnd, GWL_EXSTYLE, extendedStyle | WS_EX_TRANSPARENT); + } + } +} diff --git a/MainApp/AppWindow/NumPad.xaml b/MainApp/AppWindow/NumPad.xaml new file mode 100644 index 0000000..7cc9de0 --- /dev/null +++ b/MainApp/AppWindow/NumPad.xaml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + Virtual NumPad + + +