From d2d529b26c40794ea8b3dbaa785eea8fc6c87cbe Mon Sep 17 00:00:00 2001 From: Helios Developer Edition Date: Thu, 15 Oct 2026 16:26:38 -0500 Subject: [PATCH 1/5] Helios Developer Edition: profile editor improvements and side-by-side installer - Find All / Replace with results window, Go To, Replace, Replace All - Ctrl+wheel zooms toward the pointer; side wheel and Shift+wheel scroll horizontally - Replacing an image keeps the control size (editor only) - Group (Ctrl+G) / Ungroup (Ctrl+U) - Typing Width/Height scales contents proportionally; rotation-aware child scaling - Bindings panel lists actions under Monitors alphabetically - Renamed to "Helios Developer Edition", installs to Program Files\Helios Development with its own upgrade code so it coexists with regular Helios Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01LWgErTaLTgwjAQP5E7GL1Z --- Helios Installer/Helios Installer.vdproj | 20 +- Helios Installer/Helios32bit Installer.vdproj | 20 +- Helios Installer/HeliosInstallAdjustments.vbs | 11 +- Helios/Controls/ImageDecorationBase.cs | 42 +- Helios/Controls/ImageTranslucent.cs | 42 +- Helios/Controls/KneeBoard.cs | 3 +- Helios/HeliosVisualContainer.cs | 23 +- Helios/UndoManager.cs | 6 + HeliosInstallAdjustments.vbs | 9 +- Profile Editor/ArchiveInstall/Welcome.xaml | 2 +- Profile Editor/BindingsPanel.xaml.cs | 3 + Profile Editor/EditorMouseWheel.cs | 211 +++++++ .../FindReplace/FindResultsWindow.xaml | 70 +++ .../FindReplace/FindResultsWindow.xaml.cs | 173 ++++++ Profile Editor/FindReplace/ProfileSearch.cs | 550 ++++++++++++++++++ Profile Editor/GlobalOptionsWindow.xaml | 2 +- Profile Editor/GroupCommands.cs | 264 +++++++++ Profile Editor/HeliosVisualContainerEditor.cs | 23 + Profile Editor/MainWindow.xaml | 16 +- Profile Editor/MainWindow.xaml.cs | 162 ++++++ Profile Editor/MonitorDocument.xaml | 2 +- Profile Editor/MonitorDocument.xaml.cs | 31 +- Profile Editor/PanelDocument.xaml.cs | 31 +- Profile Editor/Profile Editor.csproj | 13 + Profile Editor/ProfileEditorCommands.cs | 4 + Profile Editor/Properties/AssemblyInfo.cs | 6 +- .../PropertyEditors/LayoutPropertyEditor.xaml | 8 +- .../LayoutPropertyEditor.xaml.cs | 76 +++ Profile Editor/ProportionalResize.cs | 82 +++ .../UndoEvents/ReparentControlUndoEvent.cs | 90 +++ .../ViewModel/AlphabeticalTreeSort.cs | 179 ++++++ 31 files changed, 2068 insertions(+), 106 deletions(-) create mode 100644 Profile Editor/EditorMouseWheel.cs create mode 100644 Profile Editor/FindReplace/FindResultsWindow.xaml create mode 100644 Profile Editor/FindReplace/FindResultsWindow.xaml.cs create mode 100644 Profile Editor/FindReplace/ProfileSearch.cs create mode 100644 Profile Editor/GroupCommands.cs create mode 100644 Profile Editor/ProportionalResize.cs create mode 100644 Profile Editor/UndoEvents/ReparentControlUndoEvent.cs create mode 100644 Profile Editor/ViewModel/AlphabeticalTreeSort.cs diff --git a/Helios Installer/Helios Installer.vdproj b/Helios Installer/Helios Installer.vdproj index d42fd95d1..a9af6e7d2 100644 --- a/Helios Installer/Helios Installer.vdproj +++ b/Helios Installer/Helios Installer.vdproj @@ -3924,7 +3924,7 @@ { "{9EF0B969-E518-4E46-987F-47570745A589}:_937487924C53439892F37CF38F2AF25A" { - "Name" = "8:Helios" + "Name" = "8:Helios Developer Edition" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" @@ -3948,7 +3948,7 @@ } "{3C67513D-01DD-4637-8A68-80971EB9504F}:_E384890BFB804E8DBFE6ADCE0EDB8172" { - "DefaultLocation" = "8:[ProgramFiles64Folder][Manufacturer]\\[ProductName]" + "DefaultLocation" = "8:[ProgramFiles64Folder]Helios Development" "Name" = "8:#1925" "AlwaysCreate" = "11:TRUE" "Condition" = "8:" @@ -4116,10 +4116,10 @@ "Product" { "Name" = "8:Microsoft Visual Studio" - "ProductName" = "8:Helios" - "ProductCode" = "8:{421509D7-A828-4190-8C5E-8E1D34BAA66F}" - "PackageCode" = "8:{531526BD-654F-45DC-B26B-0F01475637F3}" - "UpgradeCode" = "8:{589D8667-3ED9-478B-8F67-A56E4FADBC63}" + "ProductName" = "8:Helios Developer Edition" + "ProductCode" = "8:{D9EDC67F-BB34-499C-B559-F16B1A8D9425}" + "PackageCode" = "8:{9F425DB3-5AC3-4E56-8A7D-428CEAED4085}" + "UpgradeCode" = "8:{61ABF5C4-2190-4DF7-8D49-7203DFD55DE0}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" @@ -4129,7 +4129,7 @@ "Manufacturer" = "8:Helios Virtual Cockpit" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:https://github.com/HeliosVirtualCockpit/Helios/issues/" - "Title" = "8:Helios" + "Title" = "8:Helios Developer Edition" "Subject" = "8:" "ARPCONTACT" = "8:Helios Contributors" "Keywords" = "8:Helios DCS BMS Cockpit" @@ -4240,7 +4240,7 @@ { "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_04C07E0DE5FE4A0EB4EDFB607E9D253D" { - "Name" = "8:Helios Profile Editor" + "Name" = "8:Helios Developer Edition" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" @@ -4268,7 +4268,7 @@ } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_4959DA6DB3794FBA92F8B9F85C1ACFC9" { - "Name" = "8:Helios Profile Editor" + "Name" = "8:Helios Developer Edition" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" @@ -4296,7 +4296,7 @@ } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_53AFDF753B84467AB063EF6F037857AC" { - "Name" = "8:Profile Editor Debug" + "Name" = "8:Helios Developer Edition Debug" "Arguments" = "8:-l Debug" "Description" = "8:Profile Editor with Loglevel Debug" "ShowCmd" = "3:1" diff --git a/Helios Installer/Helios32bit Installer.vdproj b/Helios Installer/Helios32bit Installer.vdproj index f37ea015f..dc0091018 100644 --- a/Helios Installer/Helios32bit Installer.vdproj +++ b/Helios Installer/Helios32bit Installer.vdproj @@ -3903,7 +3903,7 @@ { "{9EF0B969-E518-4E46-987F-47570745A589}:_937487924C53439892F37CF38F2AF25A" { - "Name" = "8:Helios" + "Name" = "8:Helios Developer Edition" "AlwaysCreate" = "11:FALSE" "Condition" = "8:" "Transitive" = "11:FALSE" @@ -3927,7 +3927,7 @@ } "{3C67513D-01DD-4637-8A68-80971EB9504F}:_E384890BFB804E8DBFE6ADCE0EDB8172" { - "DefaultLocation" = "8:[ProgramFilesFolder][Manufacturer]\\[ProductName]" + "DefaultLocation" = "8:[ProgramFilesFolder]Helios Development" "Name" = "8:#1925" "AlwaysCreate" = "11:TRUE" "Condition" = "8:" @@ -4095,10 +4095,10 @@ "Product" { "Name" = "8:Microsoft Visual Studio" - "ProductName" = "8:Helios" - "ProductCode" = "8:{421509D7-A828-4190-8C5E-8E1D34BAA66F}" - "PackageCode" = "8:{14E07531-7433-4D31-8378-1E336DD197ED}" - "UpgradeCode" = "8:{589D8667-3ED9-478B-8F67-A56E4FADBC63}" + "ProductName" = "8:Helios Developer Edition" + "ProductCode" = "8:{D9EDC67F-BB34-499C-B559-F16B1A8D9425}" + "PackageCode" = "8:{C9D446CE-A9BA-4AFE-87E7-87F45B0C86F4}" + "UpgradeCode" = "8:{61ABF5C4-2190-4DF7-8D49-7203DFD55DE0}" "AspNetVersion" = "8:4.0.30319.0" "RestartWWWService" = "11:FALSE" "RemovePreviousVersions" = "11:TRUE" @@ -4108,7 +4108,7 @@ "Manufacturer" = "8:Helios Virtual Cockpit" "ARPHELPTELEPHONE" = "8:" "ARPHELPLINK" = "8:https://github.com/HeliosVirtualCockpit/Helios/issues/" - "Title" = "8:Helios" + "Title" = "8:Helios Developer Edition" "Subject" = "8:" "ARPCONTACT" = "8:Helios Contributors" "Keywords" = "8:Helios DCS BMS Cockpit" @@ -4219,7 +4219,7 @@ { "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_04C07E0DE5FE4A0EB4EDFB607E9D253D" { - "Name" = "8:Helios Profile Editor" + "Name" = "8:Helios Developer Edition" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" @@ -4247,7 +4247,7 @@ } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_4959DA6DB3794FBA92F8B9F85C1ACFC9" { - "Name" = "8:Helios Profile Editor" + "Name" = "8:Helios Developer Edition" "Arguments" = "8:" "Description" = "8:" "ShowCmd" = "3:1" @@ -4275,7 +4275,7 @@ } "{970C0BB2-C7D0-45D7-ABFA-7EC378858BC0}:_53AFDF753B84467AB063EF6F037857AC" { - "Name" = "8:Profile Editor Debug" + "Name" = "8:Helios Developer Edition Debug" "Arguments" = "8:-l Debug" "Description" = "8:Profile Editor with Loglevel Debug" "ShowCmd" = "3:1" diff --git a/Helios Installer/HeliosInstallAdjustments.vbs b/Helios Installer/HeliosInstallAdjustments.vbs index 1c1bb8a07..e8cbb1cec 100644 --- a/Helios Installer/HeliosInstallAdjustments.vbs +++ b/Helios Installer/HeliosInstallAdjustments.vbs @@ -25,6 +25,10 @@ else Dim toolsUpgrade:toolsUpgrade = "{1A6DEA70-100A-4C3B-A9D9-64340BEAC520}" Dim upgradeCode:upgradeCode = heliosUpgrade + ' upgrade code for Helios Developer Edition, which installs alongside regular Helios + ' in Program Files\Helios Development (must match UpgradeCode in the Helios installer projects) + Dim heliosDevEditionUpgrade:heliosDevEditionUpgrade = "{61ABF5C4-2190-4DF7-8D49-7203DFD55DE0}" + ' upgrade code for Helios Development Prototypes Dim heliosDevUpgrade:heliosDevUpgrade = "{BA7FFC56-BDB7-4B02-8A12-089A630CCF96}" @@ -34,7 +38,10 @@ else Dim session : Set session = installer.OpenPackage(database,1) : If Err <> 0 Then Fail "Installer: '" & msiPackage & "' has invalid installer package format" ' check product (this script is shared) - if session.Property("ProductName") = "Helios Keypress Receiver" then + if session.Property("ProductName") = "Helios Developer Edition" then + Wscript.Echo "configuring Helios Developer Edition" + upgradeCode = heliosDevEditionUpgrade + elseif session.Property("ProductName") = "Helios Keypress Receiver" then Wscript.Echo "configuring Helios Keypress Receiver Installer" upgradeCode = keypressUpgrade elseif session.Property("ProductName") = "Helios Developer Tools" then @@ -55,7 +62,7 @@ else ' run custom actions as user instead of system Execute database, "UPDATE CustomAction SET `Type` = 1025 WHERE `Type` = 3073" - if session.Property("ProductName") = "Helios" then + if session.Property("ProductName") = "Helios" or session.Property("ProductName") = "Helios Developer Edition" then if not RemoveProblemComponent( database, "PATCHING.DLL|patching.dll") then ' if lowercase was not found then try mixed case if not RemoveProblemComponent( database, "PATCHING.DLL|Patching.dll") then diff --git a/Helios/Controls/ImageDecorationBase.cs b/Helios/Controls/ImageDecorationBase.cs index 562051d38..b85396088 100644 --- a/Helios/Controls/ImageDecorationBase.cs +++ b/Helios/Controls/ImageDecorationBase.cs @@ -143,20 +143,58 @@ public string Image } else { + // when an existing image is replaced in the Profile Editor, keep the control's size and scale the + // new image to fit it; the first image a control receives (new control, profile load, template) + // still sizes the control, and outside the editor (Control Center) behavior is unchanged image = ConfigManager.ImageManager.LoadImage(_imageFile); - if (image != null) + if (image != null && (string.IsNullOrEmpty(oldValue) || !DesignMode)) { Width = image.Width; Height = image.Height; } } - OnPropertyChanged("Image", oldValue, value, true); + NotifyImageChanged(oldValue, value); Refresh(); } } } + /// + /// Report the image change. A centered image is drawn at its own size, so when an existing image is + /// replaced, alignment switches to stretched so the new image scales to the size of the control. + /// Both changes are one undo step. + /// + private void NotifyImageChanged(string oldValue, string newValue) + { + UndoManager undo = ConfigManager.UndoManager; + bool switchAlignment = !string.IsNullOrEmpty(oldValue) + && !string.IsNullOrEmpty(newValue) + && _alignment == ImageAlignment.Centered + && DesignMode + && !(undo?.Working ?? false); + bool ownBatch = switchAlignment && undo != null && !undo.InBatch; + if (ownBatch) + { + undo.StartBatch(); + } + try + { + OnPropertyChanged("Image", oldValue, newValue, true); + if (switchAlignment) + { + Alignment = ImageAlignment.Stretched; + } + } + finally + { + if (ownBatch) + { + undo.CloseBatch(); + } + } + } + public ImageAlignment Alignment { get diff --git a/Helios/Controls/ImageTranslucent.cs b/Helios/Controls/ImageTranslucent.cs index 8bf0fc808..b3598f051 100644 --- a/Helios/Controls/ImageTranslucent.cs +++ b/Helios/Controls/ImageTranslucent.cs @@ -76,8 +76,11 @@ public string Image image = refreshCapableImage.LoadImage(Image, Convert.ToInt32(Width), Convert.ToInt32(Height), LoadImageOptions.ReloadIfChangedExternally); } } - else + else if (string.IsNullOrEmpty(oldValue) || !DesignMode) { + // when an existing image is replaced in the Profile Editor, keep the control's size and scale the + // new image to fit it; the first image a control receives (new control, profile load, template) + // still sizes the control, and outside the editor (Control Center) behavior is unchanged image = ConfigManager.ImageManager.LoadImage(_imageFile); if (image != null) { @@ -90,12 +93,47 @@ public string Image Height = 100d; } } - OnPropertyChanged("Image", oldValue, value, true); + NotifyImageChanged(oldValue, value); Refresh(); } } } + /// + /// Report the image change. A centered image is drawn at its own size, so when an existing image is + /// replaced, alignment switches to stretched so the new image scales to the size of the control. + /// Both changes are one undo step. + /// + private void NotifyImageChanged(string oldValue, string newValue) + { + UndoManager undo = ConfigManager.UndoManager; + bool switchAlignment = !string.IsNullOrEmpty(oldValue) + && !string.IsNullOrEmpty(newValue) + && _alignment == ImageAlignment.Centered + && DesignMode + && !(undo?.Working ?? false); + bool ownBatch = switchAlignment && undo != null && !undo.InBatch; + if (ownBatch) + { + undo.StartBatch(); + } + try + { + OnPropertyChanged("Image", oldValue, newValue, true); + if (switchAlignment) + { + Alignment = ImageAlignment.Stretched; + } + } + finally + { + if (ownBatch) + { + undo.CloseBatch(); + } + } + } + public ImageAlignment Alignment { get diff --git a/Helios/Controls/KneeBoard.cs b/Helios/Controls/KneeBoard.cs index e52ae68f3..c40e37ce3 100644 --- a/Helios/Controls/KneeBoard.cs +++ b/Helios/Controls/KneeBoard.cs @@ -92,7 +92,8 @@ public string Image ImageSource image = ConfigManager.ImageManager.LoadImage(_positions[0].Name); //_imageFile - if (image != null) + // in the Profile Editor, only size to the image the first time; afterwards the image scales to the control + if (image != null && (string.IsNullOrEmpty(oldValue) || !DesignMode)) { Width = image.Width; Height = image.Height; diff --git a/Helios/HeliosVisualContainer.cs b/Helios/HeliosVisualContainer.cs index 9ec7ef8f0..09e26f3d8 100644 --- a/Helios/HeliosVisualContainer.cs +++ b/Helios/HeliosVisualContainer.cs @@ -40,22 +40,23 @@ public override void ScaleChildren(double scaleX, double scaleY) { foreach (HeliosVisual visual in Children) { - if (visual.Left > 0) - { - double locXDif = visual.Left; - visual.Left += (locXDif * scaleX) - locXDif; - } + // position scales with the container, including devices that hang off the left or top edge + visual.Left *= scaleX; + visual.Top *= scaleY; - if (visual.Top > 0) + // a device turned 90 degrees has its own width running along the container's height + double childScaleX = scaleX; + double childScaleY = scaleY; + if (visual.Rotation != HeliosVisualRotation.None && visual.Rotation != HeliosVisualRotation.ROT180) { - double locYDif = visual.Top; - visual.Top += (locYDif * scaleY) - locYDif; + childScaleX = scaleY; + childScaleY = scaleX; } - visual.Width = Math.Max(visual.Width * scaleX, 1d); - visual.Height = Math.Max(visual.Height * scaleY, 1d); + visual.Width = Math.Max(visual.Width * childScaleX, 1d); + visual.Height = Math.Max(visual.Height * childScaleY, 1d); - visual.ScaleChildren(scaleX, scaleY); + visual.ScaleChildren(childScaleX, childScaleY); } } diff --git a/Helios/UndoManager.cs b/Helios/UndoManager.cs index 7991d5dda..a07f54ce9 100644 --- a/Helios/UndoManager.cs +++ b/Helios/UndoManager.cs @@ -62,6 +62,12 @@ public UndoManager() /// public bool CanRedo => _redoEvents.Count > 0 && !Working && _batch == null; + /// + /// Gets a value indicating whether a batch is currently open, so callers that want to group + /// several changes into one undo step know whether they need to open (and close) their own. + /// + public bool InBatch => _batch != null; + /// /// Rollback the last command. /// diff --git a/HeliosInstallAdjustments.vbs b/HeliosInstallAdjustments.vbs index 6e4b72b85..75c300566 100644 --- a/HeliosInstallAdjustments.vbs +++ b/HeliosInstallAdjustments.vbs @@ -26,6 +26,10 @@ else Dim heliosF15EPluginUpgrade:heliosF15EPluginUpgrade = "{CCACC9F3-3F71-4CE5-8A7E-3CCF3746FC46}" Dim upgradeCode:upgradeCode = heliosUpgrade + ' upgrade code for Helios Developer Edition, which installs alongside regular Helios + ' in Program Files\Helios Development (must match UpgradeCode in the Helios installer projects) + Dim heliosDevEditionUpgrade:heliosDevEditionUpgrade = "{61ABF5C4-2190-4DF7-8D49-7203DFD55DE0}" + ' upgrade code for Helios Development Prototypes Dim heliosDevUpgrade:heliosDevUpgrade = "{BA7FFC56-BDB7-4B02-8A12-089A630CCF96}" Dim heliosDevF15EPluginUpgrade:heliosDevF15EPluginUpgrade = "{FF1DE3B4-D0F2-4BC6-AE4C-DCD07C7FFF24}" @@ -36,7 +40,10 @@ else Dim session : Set session = installer.OpenPackage(database,1) : If Err <> 0 Then Fail "Installer: '" & msiPackage & "' has invalid installer package format" ' check product (this script is shared) - if session.Property("ProductName") = "Helios Keypress Receiver" then + if session.Property("ProductName") = "Helios Developer Edition" then + Wscript.Echo "configuring Helios Developer Edition" + upgradeCode = heliosDevEditionUpgrade + elseif session.Property("ProductName") = "Helios Keypress Receiver" then Wscript.Echo "configuring Helios Keypress Receiver Installer" upgradeCode = keypressUpgrade elseif session.Property("ProductName") = "Helios Developer Tools" then diff --git a/Profile Editor/ArchiveInstall/Welcome.xaml b/Profile Editor/ArchiveInstall/Welcome.xaml index acd9a69d2..523469c7e 100644 --- a/Profile Editor/ArchiveInstall/Welcome.xaml +++ b/Profile Editor/ArchiveInstall/Welcome.xaml @@ -26,7 +26,7 @@ Work through the Interface Status until all the interfaces shown there have a green "check mark" status. Use the button on the right to get started. When you are done, return here via the "Installlation Mode" tab shown at the bottom.