From dbc732f1e50837dec9af956e8e44afeb205d6609 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 11 Apr 2026 16:42:45 +0200 Subject: [PATCH 01/18] Added multiple window layouts --- TombEditor/Command.cs | 6 ++ TombEditor/Configuration.cs | 8 ++ TombEditor/Editor.cs | 10 ++ TombEditor/Forms/FormMain.Designer.cs | 20 ++-- TombEditor/Forms/FormMain.cs | 137 +++++++++++++++++++++++++- 5 files changed, 170 insertions(+), 11 deletions(-) diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs index 05f67389a..a9ca567de 100644 --- a/TombEditor/Command.cs +++ b/TombEditor/Command.cs @@ -1734,6 +1734,12 @@ static CommandHandler() args.Editor.ConfigurationChange(); }); + for (int i = 1; i <= 9; i++) + { + int index = i; + AddCommand("SwitchLayout" + i, "Switch to layout " + i, CommandType.Windows, (CommandArgs args) => args.Editor.SwitchLayout(index)); + } + AddCommand("DrawPortals", "Draw portals", CommandType.View, delegate (CommandArgs args) { args.Editor.Configuration.Rendering3D_ShowPortals = !args.Editor.Configuration.Rendering3D_ShowPortals; diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index e1f44ed84..dd0953f7c 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -300,6 +300,8 @@ public class Configuration : ConfigurationBase public bool Window_FormMaterialEditor_Maximized { get; set; } = false; public DockPanelState Window_Layout { get; set; } = Window_LayoutDefault; + public List Window_CustomLayouts { get; set; } = new List(); + public string Window_ActiveLayoutName { get; set; } = string.Empty; public void EnsureDefaults() { @@ -420,4 +422,10 @@ public void EnsureDefaults() } }; } + + public class NamedLayout + { + public string Name { get; set; } = string.Empty; + public DockPanelState State { get; set; } = new DockPanelState(); + } } diff --git a/TombEditor/Editor.cs b/TombEditor/Editor.cs index 4fde87dce..7ee822b80 100644 --- a/TombEditor/Editor.cs +++ b/TombEditor/Editor.cs @@ -849,6 +849,16 @@ public void ToggleToolWindow(Type contentType) RaiseEvent(new ToolWindowToggleEvent() { ContentType = contentType }); } + // Layout switch event + public class SwitchLayoutEvent : IEditorEvent + { + public int LayoutIndex { get; internal set; } + } + public void SwitchLayout(int layoutIndex) + { + RaiseEvent(new SwitchLayoutEvent() { LayoutIndex = layoutIndex }); + } + // Default control engage event public class DefaultControlActivationEvent : IEditorEvent { diff --git a/TombEditor/Forms/FormMain.Designer.cs b/TombEditor/Forms/FormMain.Designer.cs index d11188866..bd48277ea 100644 --- a/TombEditor/Forms/FormMain.Designer.cs +++ b/TombEditor/Forms/FormMain.Designer.cs @@ -192,7 +192,7 @@ private void InitializeComponent() toolStripMenuItem6 = new ToolStripMenuItem(); butFindMenu = new ToolStripMenuItem(); windowToolStripMenuItem = new ToolStripMenuItem(); - restoreDefaultLayoutToolStripMenuItem = new ToolStripMenuItem(); + layoutsToolStripMenuItem = new ToolStripMenuItem(); toolStripMenuSeparator14 = new ToolStripSeparator(); sectorOptionsToolStripMenuItem = new ToolStripMenuItem(); roomOptionsToolStripMenuItem = new ToolStripMenuItem(); @@ -1877,20 +1877,20 @@ private void InitializeComponent() // windowToolStripMenuItem // windowToolStripMenuItem.BackColor = System.Drawing.Color.FromArgb(60, 63, 65); - windowToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { restoreDefaultLayoutToolStripMenuItem, toolStripMenuSeparator14, sectorOptionsToolStripMenuItem, roomOptionsToolStripMenuItem, itemBrowserToolStripMenuItem, importedGeometryBrowserToolstripMenuItem, contentBrowserToolStripMenuItem, triggerListToolStripMenuItem, lightingToolStripMenuItem, paletteToolStripMenuItem, texturePanelToolStripMenuItem, objectListToolStripMenuItem, statisticsToolStripMenuItem, flybyTimelineToolStripMenuItem, dockableToolStripMenuItem, floatingToolStripMenuItem }); + windowToolStripMenuItem.DropDownItems.AddRange(new ToolStripItem[] { layoutsToolStripMenuItem, toolStripMenuSeparator14, sectorOptionsToolStripMenuItem, roomOptionsToolStripMenuItem, itemBrowserToolStripMenuItem, importedGeometryBrowserToolstripMenuItem, contentBrowserToolStripMenuItem, triggerListToolStripMenuItem, lightingToolStripMenuItem, paletteToolStripMenuItem, texturePanelToolStripMenuItem, objectListToolStripMenuItem, statisticsToolStripMenuItem, flybyTimelineToolStripMenuItem, dockableToolStripMenuItem, floatingToolStripMenuItem }); windowToolStripMenuItem.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220); windowToolStripMenuItem.Name = "windowToolStripMenuItem"; windowToolStripMenuItem.Size = new System.Drawing.Size(63, 25); windowToolStripMenuItem.Text = "Window"; // - // restoreDefaultLayoutToolStripMenuItem + // layoutsToolStripMenuItem // - restoreDefaultLayoutToolStripMenuItem.BackColor = System.Drawing.Color.FromArgb(60, 63, 65); - restoreDefaultLayoutToolStripMenuItem.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220); - restoreDefaultLayoutToolStripMenuItem.Name = "restoreDefaultLayoutToolStripMenuItem"; - restoreDefaultLayoutToolStripMenuItem.Size = new System.Drawing.Size(246, 22); - restoreDefaultLayoutToolStripMenuItem.Text = "Restore default layout"; - restoreDefaultLayoutToolStripMenuItem.Click += restoreDefaultLayoutToolStripMenuItem_Click; + layoutsToolStripMenuItem.BackColor = System.Drawing.Color.FromArgb(60, 63, 65); + layoutsToolStripMenuItem.ForeColor = System.Drawing.Color.FromArgb(220, 220, 220); + layoutsToolStripMenuItem.Name = "layoutsToolStripMenuItem"; + layoutsToolStripMenuItem.Size = new System.Drawing.Size(246, 22); + layoutsToolStripMenuItem.Text = "Layouts"; + layoutsToolStripMenuItem.DropDownOpening += layoutsToolStripMenuItem_DropDownOpening; // // toolStripMenuSeparator14 // @@ -2328,7 +2328,7 @@ private void InitializeComponent() private DarkUI.Docking.DarkDockPanel dockArea; private System.Windows.Forms.Panel panelDockArea; private System.Windows.Forms.ToolStripMenuItem windowToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem restoreDefaultLayoutToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem layoutsToolStripMenuItem; private System.Windows.Forms.ToolStripSeparator toolStripMenuSeparator14; private System.Windows.Forms.ToolStripMenuItem sectorOptionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem roomOptionsToolStripMenuItem; diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 1ebb35f37..4ab555211 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -336,6 +336,14 @@ obj is Editor.SelectedRoomChangedEvent || if (obj is Editor.ToolWindowToggleEvent) ToolWindow_Toggle(GetWindow((obj as Editor.ToolWindowToggleEvent).ContentType.FullName) as DarkToolWindow); + if (obj is Editor.SwitchLayoutEvent) + { + var evt = (Editor.SwitchLayoutEvent)obj; + var layouts = _editor.Configuration.Window_CustomLayouts; + if (evt.LayoutIndex > 0 && evt.LayoutIndex <= layouts.Count) + Layout_SwitchTo(layouts[evt.LayoutIndex - 1].Name); + } + if (obj is Editor.LevelFileNameChangedEvent) RefreshRecentProjectsList(); @@ -573,9 +581,136 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) return base.ProcessCmdKey(ref msg, keyData); } - private void restoreDefaultLayoutToolStripMenuItem_Click(object sender, EventArgs e) + private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e) + { + layoutsToolStripMenuItem.DropDownItems.Clear(); + + var config = _editor.Configuration; + bool hasCustomLayouts = config.Window_CustomLayouts.Count > 0; + bool hasActiveLayout = !string.IsNullOrEmpty(config.Window_ActiveLayoutName); + + // Default layout entry. + var defaultItem = new ToolStripMenuItem("Default"); + defaultItem.Checked = !hasActiveLayout; + defaultItem.Click += (s, ev) => Layout_RestoreDefault(); + layoutsToolStripMenuItem.DropDownItems.Add(defaultItem); + + // Custom layout entries. + if (hasCustomLayouts) + { + layoutsToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator()); + + for (int i = 0; i < config.Window_CustomLayouts.Count; i++) + { + var layout = config.Window_CustomLayouts[i]; + var item = new ToolStripMenuItem(layout.Name); + item.Checked = layout.Name == config.Window_ActiveLayoutName; + + if (i < 9) + { + var hotkeys = config.UI_Hotkeys["SwitchLayout" + (i + 1)]; + item.ShortcutKeyDisplayString = string.Join(", ", hotkeys.Select(h => h.ToString()).Where(str => !string.IsNullOrWhiteSpace(str))); + } + + string layoutName = layout.Name; + item.Click += (s, ev) => Layout_SwitchTo(layoutName); + layoutsToolStripMenuItem.DropDownItems.Add(item); + } + } + + // Management entries. + layoutsToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator()); + + var saveItem = new ToolStripMenuItem("Save layout"); + saveItem.Click += (s, ev) => + { + if (hasActiveLayout) + Layout_SaveCurrent(); + else + Layout_SaveAs(); + }; + layoutsToolStripMenuItem.DropDownItems.Add(saveItem); + + var saveAsItem = new ToolStripMenuItem("Save layout as..."); + saveAsItem.Click += (s, ev) => Layout_SaveAs(); + layoutsToolStripMenuItem.DropDownItems.Add(saveAsItem); + + var deleteItem = new ToolStripMenuItem("Delete layout"); + deleteItem.Enabled = hasActiveLayout; + deleteItem.Click += (s, ev) => Layout_Delete(); + layoutsToolStripMenuItem.DropDownItems.Add(deleteItem); + } + + private void Layout_RestoreDefault() + { + _editor.Configuration.Window_ActiveLayoutName = string.Empty; + LoadWindowLayout(new Configuration()); + } + + private void Layout_SwitchTo(string name) + { + var layout = _editor.Configuration.Window_CustomLayouts.FirstOrDefault(l => l.Name == name); + if (layout == null) + return; + + _editor.Configuration.Window_ActiveLayoutName = name; + _editor.Configuration.Window_Layout = layout.State; + LoadWindowLayout(_editor.Configuration); + } + + private void Layout_SaveCurrent() { + var config = _editor.Configuration; + var layout = config.Window_CustomLayouts.FirstOrDefault(l => l.Name == config.Window_ActiveLayoutName); + if (layout == null) + return; + + layout.State = dockArea.GetDockPanelState(); + config.Window_Layout = layout.State; + _editor.ConfigurationChange(false, false, false, true); + } + + private void Layout_SaveAs() + { + using (var form = new FormInputBox("Save layout", "Enter layout name:")) + { + if (form.ShowDialog(this) != DialogResult.OK || string.IsNullOrWhiteSpace(form.Result)) + return; + + string name = form.Result.Trim(); + var config = _editor.Configuration; + + if (config.Window_CustomLayouts.Any(l => l.Name.Equals(name, StringComparison.OrdinalIgnoreCase))) + { + DarkMessageBox.Show(this, "A layout with this name already exists.", "Save layout", + MessageBoxButtons.OK, MessageBoxIcon.Warning); + return; + } + + var newLayout = new NamedLayout + { + Name = name, + State = dockArea.GetDockPanelState() + }; + + config.Window_CustomLayouts.Add(newLayout); + config.Window_ActiveLayoutName = name; + config.Window_Layout = newLayout.State; + _editor.ConfigurationChange(false, false, false, true); + } + } + + private void Layout_Delete() + { + var config = _editor.Configuration; + var layout = config.Window_CustomLayouts.FirstOrDefault(l => l.Name == config.Window_ActiveLayoutName); + if (layout == null) + return; + + config.Window_CustomLayouts.Remove(layout); + config.Window_ActiveLayoutName = string.Empty; LoadWindowLayout(new Configuration()); + _editor.ConfigurationChange(false, false, false, true); } private void ToolWindow_Toggle(DarkToolWindow toolWindow) From 6c27626f0f1ae247060470ab3796ed19c5f31745 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:42:01 +0200 Subject: [PATCH 02/18] Layout fixes --- TombEditor/Controls/Panel2DMap.cs | 36 +++++++++-------- TombEditor/Controls/Panel3D/Panel3D.cs | 34 +++------------- .../Controls/Panel3D/Panel3DCameraMovement.cs | 14 +++---- TombEditor/Controls/Panel3D/Panel3DDraw.cs | 1 - TombEditor/Controls/Panel3D/Panel3DInit.cs | 38 +++++++++++++++--- TombEditor/ToolWindows/MainView.cs | 40 ++++++++++--------- .../TombLib.Forms/Controls/RenderingPanel.cs | 2 + 7 files changed, 88 insertions(+), 77 deletions(-) diff --git a/TombEditor/Controls/Panel2DMap.cs b/TombEditor/Controls/Panel2DMap.cs index e2ff17947..af43ef874 100644 --- a/TombEditor/Controls/Panel2DMap.cs +++ b/TombEditor/Controls/Panel2DMap.cs @@ -41,15 +41,15 @@ public float ViewScale } private float _viewScale = 6.0f; - private readonly DepthBar _depthBar; - private readonly Editor _editor; + private DepthBar _depthBar; + private Editor _editor; private Room _roomMouseClicked; private HashSet _roomsToMove; // Set to a valid list only if room dragging is active private Vector2 _roomMouseOffset; // Relative vector to the position of the room for where it was clicked. private Vector2? _viewMoveMouseWorldCoord; private int? _currentlyEditedDepthProbeIndex; private Point _lastMousePosition; - private readonly MovementTimer _movementTimer; + private MovementTimer _movementTimer; private IReadOnlyList _insertionContourLineData; private Vector2 _insertionDropPosition; private VectorInt2 _insertionCurrentOffset; @@ -102,28 +102,32 @@ public Panel2DMap() AllowDrop = true; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Selectable, true); UpdateStyles(); + } - if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) - { - _editor = Editor.Instance; - _editor.EditorEventRaised += EditorEventRaised; + public void Initialize() + { + if (LicenseManager.UsageMode != LicenseUsageMode.Runtime) + return; - _depthBar = new DepthBar(_editor); - _depthBar.InvalidateParent += Invalidate; - _depthBar.GetParent += () => this; - _depthBar.SelectedRoom += rooms => _editor.SelectRoomsAndResetCamera(WinFormsUtils.BoolCombine(_editor.SelectedRooms, rooms, ModifierKeys)); + _editor = Editor.Instance; + _editor.EditorEventRaised += EditorEventRaised; - _movementTimer = new MovementTimer(MoveTimerTick); + _depthBar = new DepthBar(_editor); + _depthBar.InvalidateParent += Invalidate; + _depthBar.GetParent += () => this; + _depthBar.SelectedRoom += rooms => _editor.SelectRoomsAndResetCamera(WinFormsUtils.BoolCombine(_editor.SelectedRooms, rooms, ModifierKeys)); - UpdateBrushes(); - ResetView(); - } + _movementTimer = new MovementTimer(MoveTimerTick); + + UpdateBrushes(); + ResetView(); } protected override void Dispose(bool disposing) { - if (disposing) + if (disposing && _editor is not null) _editor.EditorEventRaised -= EditorEventRaised; + _movementTimer?.Dispose(); _insertionContourLineData = null; _currentContextMenu?.Dispose(); diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index c7fa5ef65..cd77a1fd0 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -22,7 +22,7 @@ namespace TombEditor.Controls.Panel3D { public partial class Panel3D : RenderingPanel { - private static readonly KeyMessageFilter filter = new KeyMessageFilter(); + private static readonly KeyMessageFilter _filter = new KeyMessageFilter(); [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public Camera Camera { get; set; } @@ -92,8 +92,8 @@ public bool DisablePickingForHiddenRooms private bool _disablePickingForHiddenRooms = false; // Overall state - private readonly Editor _editor; - private readonly Func _getViewportCamera; + private Editor _editor; + private Func _getViewportCamera; private Vector3? _currentRoomLastPos; // Camera state @@ -103,7 +103,7 @@ public bool DisablePickingForHiddenRooms private Vector2 _nextCameraRot; private float _lastCameraDist; private float _nextCameraDist; - private readonly Timer _flyModeTimer; + private Timer _flyModeTimer; private Camera _oldCamera; private Frustum _frustum; private Matrix4x4 _viewProjection; @@ -121,7 +121,7 @@ public bool DisablePickingForHiddenRooms private bool _gizmoEnabled = false; private BaseContextMenu _currentContextMenu; private ToolHandler _toolHandler; - private readonly MovementTimer _movementTimer; + private MovementTimer _movementTimer; private bool _dragObjectPicked = false; private bool _dragObjectMoved = false; private HighlightedObjects _highlightedObjects = HighlightedObjects.Create(null); @@ -177,7 +177,7 @@ public bool DisablePickingForHiddenRooms private RenderingTextureAllocator _renderingTextures; private RenderingTextureAllocator _fontTexture; private RenderingFont _fontDefault; - private readonly Cache _renderingCachedRooms; + private Cache _renderingCachedRooms; // Render stats private readonly Stopwatch _watch = new Stopwatch(); @@ -188,29 +188,7 @@ public bool DisablePickingForHiddenRooms public Panel3D() { - Application.AddMessageFilter(filter); - _getViewportCamera = () => Camera; - SetStyle(ControlStyles.Selectable | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); - - if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) - { - _editor = Editor.Instance; - _editor.EditorEventRaised += EditorEventRaised; - _editor.GetViewportCamera = _getViewportCamera; - - _frustum = new Frustum(); - _viewProjection = Matrix4x4.Identity; - - _toolHandler = new ToolHandler(this); - _movementTimer = new MovementTimer(MoveTimer_Tick); - - _flyModeTimer = new Timer { Interval = 1 }; - _flyModeTimer.Tick += FlyModeTimer_Tick; - - _renderingCachedRooms = new Cache(1024, CacheRoom); - } - } protected override void Dispose(bool disposing) diff --git a/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs b/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs index 93e9643eb..0b542cc12 100644 --- a/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs +++ b/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs @@ -69,7 +69,7 @@ private void MoveTimer_Tick(object sender, EventArgs e) private void FlyModeTimer_Tick(object sender, EventArgs e) { - if (_lastWindow != GetForegroundWindow() || filter.IsKeyPressed(Keys.Escape)) + if (_lastWindow != GetForegroundWindow() || _filter.IsKeyPressed(Keys.Escape)) { ToggleFlyMode(false); _lastWindow = GetForegroundWindow(); @@ -93,22 +93,22 @@ private void FlyModeTimer_Tick(object sender, EventArgs e) else if (ModifierKeys.HasFlag(Keys.Control)) cameraMoveSpeed /= 2; - if (filter.IsKeyPressed(Keys.W)) + if (_filter.IsKeyPressed(Keys.W)) newCameraPos.Z -= cameraMoveSpeed; - if (filter.IsKeyPressed(Keys.A)) + if (_filter.IsKeyPressed(Keys.A)) newCameraPos.X += cameraMoveSpeed; - if (filter.IsKeyPressed(Keys.S)) + if (_filter.IsKeyPressed(Keys.S)) newCameraPos.Z += cameraMoveSpeed; - if (filter.IsKeyPressed(Keys.D)) + if (_filter.IsKeyPressed(Keys.D)) newCameraPos.X -= cameraMoveSpeed; - if (filter.IsKeyPressed(Keys.E)) + if (_filter.IsKeyPressed(Keys.E)) newCameraPos.Y += cameraMoveSpeed; - if (filter.IsKeyPressed(Keys.Q)) + if (_filter.IsKeyPressed(Keys.Q)) newCameraPos.Y -= cameraMoveSpeed; Camera.MoveCameraPlane(newCameraPos); diff --git a/TombEditor/Controls/Panel3D/Panel3DDraw.cs b/TombEditor/Controls/Panel3D/Panel3DDraw.cs index e213b5e5f..db661bb9a 100644 --- a/TombEditor/Controls/Panel3D/Panel3DDraw.cs +++ b/TombEditor/Controls/Panel3D/Panel3DDraw.cs @@ -8,7 +8,6 @@ using TombLib.Controls; using TombLib.Graphics; using TombLib.Graphics.Primitives; - using TombLib.LevelData; using TombLib.LevelData.SectorEnums; using TombLib.LevelData.SectorEnums.Extensions; diff --git a/TombEditor/Controls/Panel3D/Panel3DInit.cs b/TombEditor/Controls/Panel3D/Panel3DInit.cs index a94d59518..dbb4a737f 100644 --- a/TombEditor/Controls/Panel3D/Panel3DInit.cs +++ b/TombEditor/Controls/Panel3D/Panel3DInit.cs @@ -1,17 +1,43 @@ using SharpDX.Toolkit.Graphics; +using System.ComponentModel; using System.Numerics; -using TombLib.Graphics.Primitives; -using TombLib.Graphics; +using System.Windows.Forms; using TombLib; +using TombLib.Controls; +using TombLib.Graphics; +using TombLib.Graphics.Primitives; using TombLib.LevelData; using TombLib.Rendering; -using TombLib.Controls; -using System; +using TombLib.Utils; namespace TombEditor.Controls.Panel3D { public partial class Panel3D { + public void Initialize() + { + if (LicenseManager.UsageMode != LicenseUsageMode.Runtime) + return; + + _getViewportCamera = () => Camera; + + _editor = Editor.Instance; + _editor.EditorEventRaised += EditorEventRaised; + _editor.GetViewportCamera = _getViewportCamera; + + _frustum = new Frustum(); + _viewProjection = Matrix4x4.Identity; + + _toolHandler = new ToolHandler(this); + _movementTimer = new MovementTimer(MoveTimer_Tick); + + _flyModeTimer = new Timer { Interval = 1 }; + _flyModeTimer.Tick += FlyModeTimer_Tick; + + _renderingCachedRooms = new Cache(1024, CacheRoom); + Application.AddMessageFilter(_filter); + } + public override void InitializeRendering(RenderingDevice device, bool antialias, ObjectRenderingQuality objectQuality) { base.InitializeRendering(device, antialias, objectQuality); @@ -42,8 +68,8 @@ public override void InitializeRendering(RenderingDevice device, bool antialias, int atlasSize = objectQuality switch { ObjectRenderingQuality.High => 4096, - ObjectRenderingQuality.Medium => 1024, - _ => 512 + ObjectRenderingQuality.Medium => 1024, + _ => 512 }; int maxAllocationSize = objectQuality switch diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index 80fbc6da7..ee740d572 100644 --- a/TombEditor/ToolWindows/MainView.cs +++ b/TombEditor/ToolWindows/MainView.cs @@ -45,6 +45,8 @@ public MainView() public void InitializeRendering(RenderingDevice device) { + panel2DMap.Initialize(); + panel3D.Initialize(); panel3D.InitializeRendering(device, _editor.Configuration.Rendering3D_Antialias, (TombLib.Controls.ObjectRenderingQuality)_editor.Configuration.Rendering3D_ObjectQuality); } @@ -106,16 +108,8 @@ private void EditorEventRaised(IEditorEvent obj) if (obj is Editor.StepHeightChangedEvent) UpdateStepHeightCombo(); - if (obj is Editor.StatisticsChangedEvent || - obj is Editor.ConfigurationChangedEvent) + if (obj is Editor.StatisticsChangedEvent) { - UpdateStatistics(); - - if (obj is Editor.ConfigurationChangedEvent) - { - panelStepHeightOptions.Visible = _editor.IsPreciseGeometryAllowed; - UpdateBottomPanelVisibility(); - } } if (obj is Editor.ConfigurationChangedEvent) @@ -132,6 +126,7 @@ private void EditorEventRaised(IEditorEvent obj) } RefreshControls(_editor.Configuration); + UpdateBottomPanelVisibility(_editor.Configuration); } // Gray out menu options that do not apply @@ -175,13 +170,13 @@ private void EditorEventRaised(IEditorEvent obj) butOpacityNone.Enabled = butOpacitySolidFaces.Enabled = butOpacityTraversableFaces.Enabled = portal != null; - butMirror.Enabled = portal != null && _editor.Level.IsTombEngine; + butMirror.Enabled = portal != null && _editor.Level.IsTombEngine; butOpacityNone.Checked = portal != null && portal.Opacity == PortalOpacity.None; butOpacitySolidFaces.Checked = portal != null && portal.Opacity == PortalOpacity.SolidFaces; butOpacityTraversableFaces.Checked = portal != null && portal.Opacity == PortalOpacity.TraversableFaces; - butMirror.Checked = portal != null && portal.Effect == PortalEffectType.ClassicMirror; + butMirror.Checked = portal != null && portal.Effect == PortalEffectType.ClassicMirror; } // Dismiss any messages @@ -200,8 +195,7 @@ obj is Editor.LevelChangedEvent || butDrawVolumes.Enabled = _editor.Level.IsTombEngine; // We may safely hide it because it's not customizable butAddSprite.Enabled = _editor.Level.Settings.GameVersion.Native() <= TRVersion.Game.TR2; - panelStepHeightOptions.Visible = _editor.IsPreciseGeometryAllowed; - UpdateBottomPanelVisibility(); + UpdateBottomPanelVisibility(_editor.Configuration); UpdateStepHeightCombo(); } @@ -282,15 +276,23 @@ _editor.SelectedObject is SinkInstance || panel3D.Invalidate(); - panelFlybyTimeline.Visible = settings.UI_ShowFlybyTimeline; - tbStats.Visible = settings.UI_ShowStats; - UpdateBottomPanelVisibility(); + UpdateBottomPanelVisibility(settings); } - private void UpdateBottomPanelVisibility() + private void UpdateBottomPanelVisibility(Configuration settings) { - panelBottomStatus.Visible = tbStats.Visible || panelStepHeightOptions.Visible; - panelBottom.Visible = panelFlybyTimeline.Visible || panelBottomStatus.Visible; + bool bottomPanelVisible = settings.UI_ShowStats || _editor.IsPreciseGeometryAllowed; + bool timelinePanelVisible = settings.UI_ShowFlybyTimeline; + + UpdateStatistics(); + + panelBottom.Visible = timelinePanelVisible || bottomPanelVisible; + panelBottomStatus.Visible = bottomPanelVisible; + + panelStepHeightOptions.Visible = _editor.IsPreciseGeometryAllowed; + tbStats.Visible = settings.UI_ShowStats; + + panelFlybyTimeline.Visible = settings.UI_ShowFlybyTimeline; } private void UpdateToolStripLayout() diff --git a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs index 7c6f59694..b0ea043b1 100644 --- a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs +++ b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs @@ -84,6 +84,8 @@ protected override void OnResize(EventArgs e) if (SwapChain != null) { SwapChain.Resize(new VectorInt2(ClientSize.Width, ClientSize.Height)); + SwapChain.Clear(ClearColor); + SwapChain.Present(); Invalidate(); } } From 23e66a40d8f428250a590ac6dbd2367c94919fea Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:33:04 +0200 Subject: [PATCH 03/18] Don't refresh layout on save or delete --- TombEditor/Forms/FormMain.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 4ab555211..539d37846 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -667,7 +667,6 @@ private void Layout_SaveCurrent() layout.State = dockArea.GetDockPanelState(); config.Window_Layout = layout.State; - _editor.ConfigurationChange(false, false, false, true); } private void Layout_SaveAs() @@ -696,7 +695,6 @@ private void Layout_SaveAs() config.Window_CustomLayouts.Add(newLayout); config.Window_ActiveLayoutName = name; config.Window_Layout = newLayout.State; - _editor.ConfigurationChange(false, false, false, true); } } @@ -710,7 +708,6 @@ private void Layout_Delete() config.Window_CustomLayouts.Remove(layout); config.Window_ActiveLayoutName = string.Empty; LoadWindowLayout(new Configuration()); - _editor.ConfigurationChange(false, false, false, true); } private void ToolWindow_Toggle(DarkToolWindow toolWindow) From ffb7f83a7bfe56a49f3834cbd7b7728465dc62a4 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 11 Apr 2026 21:34:46 +0200 Subject: [PATCH 04/18] Update Changes.txt --- Installer/Changes.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Installer/Changes.txt b/Installer/Changes.txt index a448db8e8..949c1d961 100644 --- a/Installer/Changes.txt +++ b/Installer/Changes.txt @@ -6,6 +6,7 @@ Tomb Editor: * Added Content Browser tool window for more streamlined object management. * Added Flyby Timeline control to edit and preview flyby sequences. * Added "Preview flyby sequence" and "Preview camera" context menus for cameras. + * Added multiple window layout support. Version 1.11 ============ From d3f0de9271ed7f52a8998539da94d1233ecf5c2d Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 12 Apr 2026 09:56:50 +0200 Subject: [PATCH 05/18] Address Copilot comments --- TombEditor/Controls/Panel3D/Panel3D.cs | 6 +++--- TombEditor/ToolWindows/MainView.cs | 3 +-- TombLib/TombLib.Forms/Controls/RenderingPanel.cs | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index cd77a1fd0..b012d2f3d 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -71,7 +71,7 @@ public partial class Panel3D : RenderingPanel public bool ShowSlideDirections { get { return _drawSlideDirections; } - set { if (value == _drawSlideDirections) return; _drawSlideDirections = value; _renderingCachedRooms.Clear(); } + set { if (value == _drawSlideDirections) return; _drawSlideDirections = value; _renderingCachedRooms?.Clear(); } } private bool _drawSlideDirections = false; @@ -79,7 +79,7 @@ public bool ShowSlideDirections public bool ShowIllegalSlopes { get { return _drawIllegalSlopes; } - set { if (value == _drawIllegalSlopes) return; _drawIllegalSlopes = value; _renderingCachedRooms.Clear(); } + set { if (value == _drawIllegalSlopes) return; _drawIllegalSlopes = value; _renderingCachedRooms?.Clear(); } } private bool _drawIllegalSlopes = false; @@ -87,7 +87,7 @@ public bool ShowIllegalSlopes public bool DisablePickingForHiddenRooms { get { return _disablePickingForHiddenRooms; } - set { if (value == _disablePickingForHiddenRooms) return; _disablePickingForHiddenRooms = value; _renderingCachedRooms.Clear(); } + set { if (value == _disablePickingForHiddenRooms) return; _disablePickingForHiddenRooms = value; _renderingCachedRooms?.Clear(); } } private bool _disablePickingForHiddenRooms = false; diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index ee740d572..b862e1fb2 100644 --- a/TombEditor/ToolWindows/MainView.cs +++ b/TombEditor/ToolWindows/MainView.cs @@ -109,8 +109,7 @@ private void EditorEventRaised(IEditorEvent obj) UpdateStepHeightCombo(); if (obj is Editor.StatisticsChangedEvent) - { - } + UpdateStatistics(); if (obj is Editor.ConfigurationChangedEvent) { diff --git a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs index b0ea043b1..96b9e2405 100644 --- a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs +++ b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs @@ -81,7 +81,7 @@ public Vector2 WarpMouseCursor(Point currentPosition, Point previousMousePositio protected override void OnResize(EventArgs e) { base.OnResize(e); - if (SwapChain != null) + if (SwapChain != null && SwapChain.RenderException == null) { SwapChain.Resize(new VectorInt2(ClientSize.Width, ClientSize.Height)); SwapChain.Clear(ClearColor); From 5da8b460cc9396f8c2fe58f3e229998218da4763 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:06:18 +0200 Subject: [PATCH 06/18] Remove init --- TombEditor/Controls/Panel2DMap.cs | 33 ++++++++++------------ TombEditor/Controls/Panel3D/Panel3D.cs | 21 ++++++++++++++ TombEditor/Controls/Panel3D/Panel3DInit.cs | 24 ---------------- TombEditor/ToolWindows/MainView.cs | 2 -- 4 files changed, 36 insertions(+), 44 deletions(-) diff --git a/TombEditor/Controls/Panel2DMap.cs b/TombEditor/Controls/Panel2DMap.cs index af43ef874..1bf485a4b 100644 --- a/TombEditor/Controls/Panel2DMap.cs +++ b/TombEditor/Controls/Panel2DMap.cs @@ -41,15 +41,15 @@ public float ViewScale } private float _viewScale = 6.0f; - private DepthBar _depthBar; - private Editor _editor; + private readonly DepthBar _depthBar; + private readonly Editor _editor; private Room _roomMouseClicked; private HashSet _roomsToMove; // Set to a valid list only if room dragging is active private Vector2 _roomMouseOffset; // Relative vector to the position of the room for where it was clicked. private Vector2? _viewMoveMouseWorldCoord; private int? _currentlyEditedDepthProbeIndex; private Point _lastMousePosition; - private MovementTimer _movementTimer; + private readonly MovementTimer _movementTimer; private IReadOnlyList _insertionContourLineData; private Vector2 _insertionDropPosition; private VectorInt2 _insertionCurrentOffset; @@ -102,25 +102,22 @@ public Panel2DMap() AllowDrop = true; SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Selectable, true); UpdateStyles(); - } - - public void Initialize() - { - if (LicenseManager.UsageMode != LicenseUsageMode.Runtime) - return; - _editor = Editor.Instance; - _editor.EditorEventRaised += EditorEventRaised; + if (Editor.Instance != null) + { + _editor = Editor.Instance; + _editor.EditorEventRaised += EditorEventRaised; - _depthBar = new DepthBar(_editor); - _depthBar.InvalidateParent += Invalidate; - _depthBar.GetParent += () => this; - _depthBar.SelectedRoom += rooms => _editor.SelectRoomsAndResetCamera(WinFormsUtils.BoolCombine(_editor.SelectedRooms, rooms, ModifierKeys)); + _depthBar = new DepthBar(_editor); + _depthBar.InvalidateParent += Invalidate; + _depthBar.GetParent += () => this; + _depthBar.SelectedRoom += rooms => _editor.SelectRoomsAndResetCamera(WinFormsUtils.BoolCombine(_editor.SelectedRooms, rooms, ModifierKeys)); - _movementTimer = new MovementTimer(MoveTimerTick); + _movementTimer = new MovementTimer(MoveTimerTick); - UpdateBrushes(); - ResetView(); + UpdateBrushes(); + ResetView(); + } } protected override void Dispose(bool disposing) diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index b012d2f3d..d9cdf2092 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -189,6 +189,27 @@ public bool DisablePickingForHiddenRooms public Panel3D() { SetStyle(ControlStyles.Selectable | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); + + if (Editor.Instance != null) + { + _getViewportCamera = () => Camera; + + _editor = Editor.Instance; + _editor.EditorEventRaised += EditorEventRaised; + _editor.GetViewportCamera = _getViewportCamera; + + _frustum = new Frustum(); + _viewProjection = Matrix4x4.Identity; + + _toolHandler = new ToolHandler(this); + _movementTimer = new MovementTimer(MoveTimer_Tick); + + _flyModeTimer = new Timer { Interval = 1 }; + _flyModeTimer.Tick += FlyModeTimer_Tick; + + _renderingCachedRooms = new Cache(1024, CacheRoom); + Application.AddMessageFilter(_filter); + } } protected override void Dispose(bool disposing) diff --git a/TombEditor/Controls/Panel3D/Panel3DInit.cs b/TombEditor/Controls/Panel3D/Panel3DInit.cs index dbb4a737f..4b5a26414 100644 --- a/TombEditor/Controls/Panel3D/Panel3DInit.cs +++ b/TombEditor/Controls/Panel3D/Panel3DInit.cs @@ -14,30 +14,6 @@ namespace TombEditor.Controls.Panel3D { public partial class Panel3D { - public void Initialize() - { - if (LicenseManager.UsageMode != LicenseUsageMode.Runtime) - return; - - _getViewportCamera = () => Camera; - - _editor = Editor.Instance; - _editor.EditorEventRaised += EditorEventRaised; - _editor.GetViewportCamera = _getViewportCamera; - - _frustum = new Frustum(); - _viewProjection = Matrix4x4.Identity; - - _toolHandler = new ToolHandler(this); - _movementTimer = new MovementTimer(MoveTimer_Tick); - - _flyModeTimer = new Timer { Interval = 1 }; - _flyModeTimer.Tick += FlyModeTimer_Tick; - - _renderingCachedRooms = new Cache(1024, CacheRoom); - Application.AddMessageFilter(_filter); - } - public override void InitializeRendering(RenderingDevice device, bool antialias, ObjectRenderingQuality objectQuality) { base.InitializeRendering(device, antialias, objectQuality); diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index b862e1fb2..34df2f262 100644 --- a/TombEditor/ToolWindows/MainView.cs +++ b/TombEditor/ToolWindows/MainView.cs @@ -45,8 +45,6 @@ public MainView() public void InitializeRendering(RenderingDevice device) { - panel2DMap.Initialize(); - panel3D.Initialize(); panel3D.InitializeRendering(device, _editor.Configuration.Rendering3D_Antialias, (TombLib.Controls.ObjectRenderingQuality)_editor.Configuration.Rendering3D_ObjectQuality); } From d07c0044475e40f281ee4c4817e41811c7a4e7ef Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:11:34 +0200 Subject: [PATCH 07/18] Expose layout as a const --- TombEditor/Command.cs | 6 +++--- TombEditor/Configuration.cs | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs index a9ca567de..664149a8a 100644 --- a/TombEditor/Command.cs +++ b/TombEditor/Command.cs @@ -1734,10 +1734,10 @@ static CommandHandler() args.Editor.ConfigurationChange(); }); - for (int i = 1; i <= 9; i++) + for (int i = 1; i < Configuration.MaxWindowLayouts; i++) { - int index = i; - AddCommand("SwitchLayout" + i, "Switch to layout " + i, CommandType.Windows, (CommandArgs args) => args.Editor.SwitchLayout(index)); + int currentLayoutIndex = i; + AddCommand("SwitchLayout" + i, "Switch to layout " + i, CommandType.Windows, (CommandArgs args) => args.Editor.SwitchLayout(currentLayoutIndex)); } AddCommand("DrawPortals", "Draw portals", CommandType.View, delegate (CommandArgs args) diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index dd0953f7c..accfc4157 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -13,6 +13,7 @@ namespace TombEditor // They will be loaded and saved automatically. public class Configuration : ConfigurationBase { + public const int MaxWindowLayouts = 10; public override string ConfigName { get { return "TombEditorConfiguration.xml"; } } // Global editor options From 3a043dd6bfd4666b79f9d2069fffd9de6c0c7766 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:19:28 +0200 Subject: [PATCH 08/18] Rollback readonly removal --- TombEditor/Controls/Panel3D/Panel3D.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index d9cdf2092..11aa2ed86 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -92,8 +92,8 @@ public bool DisablePickingForHiddenRooms private bool _disablePickingForHiddenRooms = false; // Overall state - private Editor _editor; - private Func _getViewportCamera; + private readonly Editor _editor; + private readonly Func _getViewportCamera; private Vector3? _currentRoomLastPos; // Camera state @@ -103,7 +103,7 @@ public bool DisablePickingForHiddenRooms private Vector2 _nextCameraRot; private float _lastCameraDist; private float _nextCameraDist; - private Timer _flyModeTimer; + private readonly Timer _flyModeTimer; private Camera _oldCamera; private Frustum _frustum; private Matrix4x4 _viewProjection; @@ -121,7 +121,7 @@ public bool DisablePickingForHiddenRooms private bool _gizmoEnabled = false; private BaseContextMenu _currentContextMenu; private ToolHandler _toolHandler; - private MovementTimer _movementTimer; + private readonly MovementTimer _movementTimer; private bool _dragObjectPicked = false; private bool _dragObjectMoved = false; private HighlightedObjects _highlightedObjects = HighlightedObjects.Create(null); @@ -177,7 +177,7 @@ public bool DisablePickingForHiddenRooms private RenderingTextureAllocator _renderingTextures; private RenderingTextureAllocator _fontTexture; private RenderingFont _fontDefault; - private Cache _renderingCachedRooms; + private readonly Cache _renderingCachedRooms; // Render stats private readonly Stopwatch _watch = new Stopwatch(); From 58e7d454f83f6e6d84bb786555cb171d0272b7f5 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sun, 12 Apr 2026 10:22:00 +0200 Subject: [PATCH 09/18] Update Panel3DInit.cs --- TombEditor/Controls/Panel3D/Panel3DInit.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/TombEditor/Controls/Panel3D/Panel3DInit.cs b/TombEditor/Controls/Panel3D/Panel3DInit.cs index 4b5a26414..5d76b2411 100644 --- a/TombEditor/Controls/Panel3D/Panel3DInit.cs +++ b/TombEditor/Controls/Panel3D/Panel3DInit.cs @@ -1,14 +1,11 @@ using SharpDX.Toolkit.Graphics; -using System.ComponentModel; using System.Numerics; -using System.Windows.Forms; using TombLib; using TombLib.Controls; using TombLib.Graphics; using TombLib.Graphics.Primitives; using TombLib.LevelData; using TombLib.Rendering; -using TombLib.Utils; namespace TombEditor.Controls.Panel3D { From 5d8167eafc601088d259a0fd72d1eb93a937148b Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:01:37 +0200 Subject: [PATCH 10/18] Address Copilot notes --- TombEditor/Command.cs | 5 +++-- TombEditor/Forms/FormMain.cs | 21 ++++++++++++------- .../TombLib.Forms/Controls/RenderingPanel.cs | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs index 664149a8a..75fd0aaae 100644 --- a/TombEditor/Command.cs +++ b/TombEditor/Command.cs @@ -1734,10 +1734,11 @@ static CommandHandler() args.Editor.ConfigurationChange(); }); - for (int i = 1; i < Configuration.MaxWindowLayouts; i++) + for (int i = 0; i < Configuration.MaxWindowLayouts; i++) { int currentLayoutIndex = i; - AddCommand("SwitchLayout" + i, "Switch to layout " + i, CommandType.Windows, (CommandArgs args) => args.Editor.SwitchLayout(currentLayoutIndex)); + int visibleLayoutIndex = i + 1; + AddCommand("SwitchLayout" + visibleLayoutIndex, "Switch to layout " + visibleLayoutIndex, CommandType.Windows, (CommandArgs args) => args.Editor.SwitchLayout(currentLayoutIndex)); } AddCommand("DrawPortals", "Draw portals", CommandType.View, delegate (CommandArgs args) diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 539d37846..be82978b4 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -340,8 +340,8 @@ obj is Editor.SelectedRoomChangedEvent || { var evt = (Editor.SwitchLayoutEvent)obj; var layouts = _editor.Configuration.Window_CustomLayouts; - if (evt.LayoutIndex > 0 && evt.LayoutIndex <= layouts.Count) - Layout_SwitchTo(layouts[evt.LayoutIndex - 1].Name); + if (evt.LayoutIndex >= 0 && evt.LayoutIndex < layouts.Count) + Layout_SwitchTo(layouts[evt.LayoutIndex].Name); } if (obj is Editor.LevelFileNameChangedEvent) @@ -606,10 +606,13 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e var item = new ToolStripMenuItem(layout.Name); item.Checked = layout.Name == config.Window_ActiveLayoutName; - if (i < 9) + if (i < Configuration.MaxWindowLayouts) { + var hotkeyName = "SwitchLayout" + (i + 1); var hotkeys = config.UI_Hotkeys["SwitchLayout" + (i + 1)]; - item.ShortcutKeyDisplayString = string.Join(", ", hotkeys.Select(h => h.ToString()).Where(str => !string.IsNullOrWhiteSpace(str))); + + if (config.UI_Hotkeys.Any(h => h.Key == hotkeyName)) + item.ShortcutKeyDisplayString = string.Join(", ", config.UI_Hotkeys[hotkeyName].Select(h => h.ToString()).Where(str => !string.IsNullOrWhiteSpace(str))); } string layoutName = layout.Name; @@ -643,8 +646,12 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e private void Layout_RestoreDefault() { + var defaultConfiguration = new Configuration(); + _editor.Configuration.Window_ActiveLayoutName = string.Empty; - LoadWindowLayout(new Configuration()); + _editor.Configuration.Window_Layout = defaultConfiguration.Window_Layout; + + LoadWindowLayout(_editor.Configuration); } private void Layout_SwitchTo(string name) @@ -706,8 +713,8 @@ private void Layout_Delete() return; config.Window_CustomLayouts.Remove(layout); - config.Window_ActiveLayoutName = string.Empty; - LoadWindowLayout(new Configuration()); + Layout_RestoreDefault(); + } private void ToolWindow_Toggle(DarkToolWindow toolWindow) diff --git a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs index 96b9e2405..ac9321b78 100644 --- a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs +++ b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs @@ -81,7 +81,7 @@ public Vector2 WarpMouseCursor(Point currentPosition, Point previousMousePositio protected override void OnResize(EventArgs e) { base.OnResize(e); - if (SwapChain != null && SwapChain.RenderException == null) + if (SwapChain != null && SwapChain.RenderException == null && ClientSize.Width > 0 && ClientSize.Height > 0) { SwapChain.Resize(new VectorInt2(ClientSize.Width, ClientSize.Height)); SwapChain.Clear(ClearColor); From 52941ab3b0c1815062cc98f23f79cde84a3396a7 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Fri, 17 Apr 2026 09:04:00 +0200 Subject: [PATCH 11/18] Address Nicky's comments --- TombEditor/Controls/Panel2DMap.cs | 2 +- TombEditor/Controls/Panel3D/Panel3D.cs | 2 +- TombEditor/Forms/FormMain.cs | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/TombEditor/Controls/Panel2DMap.cs b/TombEditor/Controls/Panel2DMap.cs index 1bf485a4b..0269da12b 100644 --- a/TombEditor/Controls/Panel2DMap.cs +++ b/TombEditor/Controls/Panel2DMap.cs @@ -103,7 +103,7 @@ public Panel2DMap() SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.Selectable, true); UpdateStyles(); - if (Editor.Instance != null) + if (Editor.Instance is not null) { _editor = Editor.Instance; _editor.EditorEventRaised += EditorEventRaised; diff --git a/TombEditor/Controls/Panel3D/Panel3D.cs b/TombEditor/Controls/Panel3D/Panel3D.cs index 11aa2ed86..52e27a41d 100644 --- a/TombEditor/Controls/Panel3D/Panel3D.cs +++ b/TombEditor/Controls/Panel3D/Panel3D.cs @@ -190,7 +190,7 @@ public Panel3D() { SetStyle(ControlStyles.Selectable | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); - if (Editor.Instance != null) + if (Editor.Instance is not null) { _getViewportCamera = () => Camera; diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index be82978b4..163a6d859 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -336,12 +336,11 @@ obj is Editor.SelectedRoomChangedEvent || if (obj is Editor.ToolWindowToggleEvent) ToolWindow_Toggle(GetWindow((obj as Editor.ToolWindowToggleEvent).ContentType.FullName) as DarkToolWindow); - if (obj is Editor.SwitchLayoutEvent) + if (obj is Editor.SwitchLayoutEvent layoutEvent) { - var evt = (Editor.SwitchLayoutEvent)obj; var layouts = _editor.Configuration.Window_CustomLayouts; - if (evt.LayoutIndex >= 0 && evt.LayoutIndex < layouts.Count) - Layout_SwitchTo(layouts[evt.LayoutIndex].Name); + if (layoutEvent.LayoutIndex >= 0 && layoutEvent.LayoutIndex < layouts.Count) + Layout_SwitchTo(layouts[layoutEvent.LayoutIndex].Name); } if (obj is Editor.LevelFileNameChangedEvent) From 8c86af8ab26dd712a7e0db1b08ab05f12c3657e0 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 10:05:30 +0200 Subject: [PATCH 12/18] Save floating toolboxes to the layout, simplify code --- TombEditor/Configuration.cs | 26 ++++++++--- TombEditor/Forms/FormMain.cs | 86 ++++++++++++++++-------------------- 2 files changed, 57 insertions(+), 55 deletions(-) diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index accfc4157..52ee0b7ae 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -1,4 +1,5 @@ -using DarkUI.Docking; +using DarkUI.Docking; +using System; using System.Collections.Generic; using System.Drawing; using System.Numerics; @@ -62,9 +63,6 @@ public class Configuration : ConfigurationBase public bool Rendering3D_InvertMouseZoom { get; set; } = false; public float Rendering3D_LineWidth { get; set; } = 10.0f; public float Rendering3D_FieldOfView { get; set; } = 50.0f; - public bool Rendering3D_ToolboxVisible { get; set; } = true; - public Point Rendering3D_ToolboxPosition { get; set; } = new Point(15, 15); - public Point Rendering3D_ObjectBrushToolboxPosition { get; set; } = new Point(50, 15); public bool Rendering3D_DisablePickingForImportedGeometry { get; set; } = false; public bool Rendering3D_DisablePickingForHiddenRooms { get; set; } = false; public bool Rendering3D_ShowPortals { get; set; } = false; @@ -300,7 +298,7 @@ public class Configuration : ConfigurationBase public Size Window_FormMaterialEditor_Size { get; set; } = new Size(537, 560); public bool Window_FormMaterialEditor_Maximized { get; set; } = false; - public DockPanelState Window_Layout { get; set; } = Window_LayoutDefault; + public NamedLayout Window_Layout { get; set; } = new NamedLayout { State = Window_LayoutDefault }; public List Window_CustomLayouts { get; set; } = new List(); public string Window_ActiveLayoutName { get; set; } = string.Empty; @@ -424,9 +422,23 @@ public void EnsureDefaults() }; } - public class NamedLayout + public class NamedLayout : ICloneable { public string Name { get; set; } = string.Empty; - public DockPanelState State { get; set; } = new DockPanelState(); + public DockPanelState State { get; set; } = Configuration.Window_LayoutDefault; + public Point ToolboxPosition { get; set; } = new Point(15, 15); + public Point ObjectBrushToolboxPosition { get; set; } = new Point(50, 15); + public bool ToolboxVisible { get; set; } = true; + + public NamedLayout Clone() => new NamedLayout + { + Name = Name, + State = State, + ToolboxPosition = ToolboxPosition, + ObjectBrushToolboxPosition = ObjectBrushToolboxPosition, + ToolboxVisible = ToolboxVisible + }; + + object ICloneable.Clone() => Clone(); } } diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 163a6d859..5209a1117 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -1,4 +1,4 @@ -using NLog; +using NLog; using DarkUI.Config; using System; using System.Collections.Generic; @@ -195,11 +195,11 @@ obj is Editor.LevelChangedEvent || if (showBrushToolbox && ObjectBrushSettings.Parent == null) { GetWindow().AddToolbox(ObjectBrushSettings); - ObjectBrushSettings.Location = _editor.Configuration.Rendering3D_ObjectBrushToolboxPosition; + ObjectBrushSettings.Location = _editor.Configuration.Window_Layout.ObjectBrushToolboxPosition; } else if (!showBrushToolbox && ObjectBrushSettings.Parent != null) { - _editor.Configuration.Rendering3D_ObjectBrushToolboxPosition = ObjectBrushSettings.Location; + _editor.Configuration.Window_Layout.ObjectBrushToolboxPosition = ObjectBrushSettings.Location; GetWindow().RemoveToolbox(ObjectBrushSettings); } } @@ -512,22 +512,17 @@ protected override void OnFormClosing(FormClosingEventArgs e) private void LoadWindowLayout(Configuration configuration) { dockArea.RemoveContent(); - dockArea.RestoreDockPanelState(configuration.Window_Layout, GetWindow); + dockArea.RestoreDockPanelState(configuration.Window_Layout.State, GetWindow); - floatingToolStripMenuItem.Checked = configuration.Rendering3D_ToolboxVisible; - ToolBox.Location = configuration.Rendering3D_ToolboxPosition; - ObjectBrushSettings.Location = configuration.Rendering3D_ObjectBrushToolboxPosition; + floatingToolStripMenuItem.Checked = configuration.Window_Layout.ToolboxVisible; + ToolBox.Location = configuration.Window_Layout.ToolboxPosition; + ObjectBrushSettings.Location = configuration.Window_Layout.ObjectBrushToolboxPosition; } private void SaveWindowLayout(Configuration configuration) { - configuration.Window_Layout = dockArea.GetDockPanelState(); - - configuration.Rendering3D_ToolboxVisible = floatingToolStripMenuItem.Checked; - configuration.Rendering3D_ToolboxPosition = ToolBox.Location; - - if (ObjectBrushSettings.Parent != null) - configuration.Rendering3D_ObjectBrushToolboxPosition = ObjectBrushSettings.Location; + SaveCurrentStateToLayout(configuration.Window_Layout); + SaveCurrentStateToActiveLayout(); } protected override bool ProcessDialogKey(Keys keyData) @@ -586,11 +581,9 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e var config = _editor.Configuration; bool hasCustomLayouts = config.Window_CustomLayouts.Count > 0; - bool hasActiveLayout = !string.IsNullOrEmpty(config.Window_ActiveLayoutName); // Default layout entry. var defaultItem = new ToolStripMenuItem("Default"); - defaultItem.Checked = !hasActiveLayout; defaultItem.Click += (s, ev) => Layout_RestoreDefault(); layoutsToolStripMenuItem.DropDownItems.Add(defaultItem); @@ -623,32 +616,21 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e // Management entries. layoutsToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator()); - var saveItem = new ToolStripMenuItem("Save layout"); - saveItem.Click += (s, ev) => - { - if (hasActiveLayout) - Layout_SaveCurrent(); - else - Layout_SaveAs(); - }; - layoutsToolStripMenuItem.DropDownItems.Add(saveItem); - var saveAsItem = new ToolStripMenuItem("Save layout as..."); saveAsItem.Click += (s, ev) => Layout_SaveAs(); layoutsToolStripMenuItem.DropDownItems.Add(saveAsItem); var deleteItem = new ToolStripMenuItem("Delete layout"); - deleteItem.Enabled = hasActiveLayout; + deleteItem.Enabled = !string.IsNullOrEmpty(config.Window_ActiveLayoutName); deleteItem.Click += (s, ev) => Layout_Delete(); layoutsToolStripMenuItem.DropDownItems.Add(deleteItem); } private void Layout_RestoreDefault() { - var defaultConfiguration = new Configuration(); - + SaveCurrentStateToActiveLayout(); _editor.Configuration.Window_ActiveLayoutName = string.Empty; - _editor.Configuration.Window_Layout = defaultConfiguration.Window_Layout; + _editor.Configuration.Window_Layout = new NamedLayout(); LoadWindowLayout(_editor.Configuration); } @@ -659,22 +641,12 @@ private void Layout_SwitchTo(string name) if (layout == null) return; + SaveCurrentStateToActiveLayout(); _editor.Configuration.Window_ActiveLayoutName = name; - _editor.Configuration.Window_Layout = layout.State; + _editor.Configuration.Window_Layout = layout.Clone(); LoadWindowLayout(_editor.Configuration); } - private void Layout_SaveCurrent() - { - var config = _editor.Configuration; - var layout = config.Window_CustomLayouts.FirstOrDefault(l => l.Name == config.Window_ActiveLayoutName); - if (layout == null) - return; - - layout.State = dockArea.GetDockPanelState(); - config.Window_Layout = layout.State; - } - private void Layout_SaveAs() { using (var form = new FormInputBox("Save layout", "Enter layout name:")) @@ -692,15 +664,12 @@ private void Layout_SaveAs() return; } - var newLayout = new NamedLayout - { - Name = name, - State = dockArea.GetDockPanelState() - }; + var newLayout = new NamedLayout { Name = name }; + SaveCurrentStateToLayout(newLayout); config.Window_CustomLayouts.Add(newLayout); config.Window_ActiveLayoutName = name; - config.Window_Layout = newLayout.State; + config.Window_Layout = newLayout.Clone(); } } @@ -713,7 +682,28 @@ private void Layout_Delete() config.Window_CustomLayouts.Remove(layout); Layout_RestoreDefault(); + } + + private void SaveCurrentStateToLayout(NamedLayout target) + { + target.State = dockArea.GetDockPanelState(); + target.ToolboxVisible = floatingToolStripMenuItem.Checked; + target.ToolboxPosition = ToolBox.Location; + if (ObjectBrushSettings.Parent != null) + target.ObjectBrushToolboxPosition = ObjectBrushSettings.Location; + } + + private void SaveCurrentStateToActiveLayout() + { + var config = _editor.Configuration; + if (string.IsNullOrEmpty(config.Window_ActiveLayoutName)) + return; + + var layout = _editor.Configuration.Window_CustomLayouts.FirstOrDefault(l => l.Name == _editor.Configuration.Window_ActiveLayoutName); + if (layout == null) + return; + SaveCurrentStateToLayout(layout); } private void ToolWindow_Toggle(DarkToolWindow toolWindow) From 661a8d87cee618f62a8be3e5d2374145624c3486 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 11:12:44 +0200 Subject: [PATCH 13/18] Address Copilot comments --- DarkUI/DarkUI/Docking/DockGroupState.cs | 16 +++++++++++++++- DarkUI/DarkUI/Docking/DockPanelState.cs | 13 ++++++++++++- DarkUI/DarkUI/Docking/DockRegionState.cs | 15 ++++++++++++++- TombEditor/Configuration.cs | 2 +- TombEditor/Forms/FormMain.cs | 2 -- 5 files changed, 42 insertions(+), 6 deletions(-) diff --git a/DarkUI/DarkUI/Docking/DockGroupState.cs b/DarkUI/DarkUI/Docking/DockGroupState.cs index bbc88b60a..cbaafdfbb 100644 --- a/DarkUI/DarkUI/Docking/DockGroupState.cs +++ b/DarkUI/DarkUI/Docking/DockGroupState.cs @@ -5,7 +5,7 @@ namespace DarkUI.Docking { - public class DockGroupState : IEquatable + public class DockGroupState : ICloneable, IEquatable { #region Property Region @@ -30,6 +30,20 @@ public DockGroupState() #endregion + #region Clone Region + + public DockGroupState Clone() => new DockGroupState + { + Contents = new List(Contents), + VisibleContent = VisibleContent, + Order = Order, + Size = Size + }; + + object ICloneable.Clone() => Clone(); + + #endregion + #region Comparison Region public bool Equals(DockGroupState other) => diff --git a/DarkUI/DarkUI/Docking/DockPanelState.cs b/DarkUI/DarkUI/Docking/DockPanelState.cs index a64e45257..68f4f7e79 100644 --- a/DarkUI/DarkUI/Docking/DockPanelState.cs +++ b/DarkUI/DarkUI/Docking/DockPanelState.cs @@ -4,7 +4,7 @@ namespace DarkUI.Docking { - public class DockPanelState : IEquatable + public class DockPanelState : ICloneable, IEquatable { #region Property Region @@ -21,6 +21,17 @@ public DockPanelState() #endregion + #region Clone Region + + public DockPanelState Clone() => new DockPanelState + { + Regions = Regions.Select(r => r.Clone()).ToList() + }; + + object ICloneable.Clone() => Clone(); + + #endregion + #region Comparison Region public bool Equals(DockPanelState other) => Regions.SequenceEqual(other.Regions); diff --git a/DarkUI/DarkUI/Docking/DockRegionState.cs b/DarkUI/DarkUI/Docking/DockRegionState.cs index 7ec1c1300..41390d99a 100644 --- a/DarkUI/DarkUI/Docking/DockRegionState.cs +++ b/DarkUI/DarkUI/Docking/DockRegionState.cs @@ -5,7 +5,7 @@ namespace DarkUI.Docking { - public class DockRegionState : IEquatable + public class DockRegionState : ICloneable, IEquatable { #region Property Region @@ -38,6 +38,19 @@ public DockRegionState(DarkDockArea area, Size size) #endregion + #region Clone Region + + public DockRegionState Clone() => new DockRegionState + { + Area = Area, + Size = Size, + Groups = Groups.Select(g => g.Clone()).ToList() + }; + + object ICloneable.Clone() => Clone(); + + #endregion + #region Comparison Region public bool Equals(DockRegionState other) => Area == other.Area && Size == other.Size && Groups.SequenceEqual(other.Groups); diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index 52ee0b7ae..466d3fb4e 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -433,7 +433,7 @@ public class NamedLayout : ICloneable public NamedLayout Clone() => new NamedLayout { Name = Name, - State = State, + State = State.Clone(), ToolboxPosition = ToolboxPosition, ObjectBrushToolboxPosition = ObjectBrushToolboxPosition, ToolboxVisible = ToolboxVisible diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 5209a1117..62832adc0 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -601,8 +601,6 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e if (i < Configuration.MaxWindowLayouts) { var hotkeyName = "SwitchLayout" + (i + 1); - var hotkeys = config.UI_Hotkeys["SwitchLayout" + (i + 1)]; - if (config.UI_Hotkeys.Any(h => h.Key == hotkeyName)) item.ShortcutKeyDisplayString = string.Join(", ", config.UI_Hotkeys[hotkeyName].Select(h => h.ToString()).Where(str => !string.IsNullOrWhiteSpace(str))); } From 7b9e7ff259e1059d2bd36f9efc71e1082c276e2c Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 11:38:45 +0200 Subject: [PATCH 14/18] Optimize layout switching speed by introducing bulk DarkUI dock updates --- DarkUI/DarkUI/Docking/DarkDockPanel.cs | 58 +++++++++++++++++++++---- DarkUI/DarkUI/Docking/DarkDockRegion.cs | 22 +++++++--- 2 files changed, 67 insertions(+), 13 deletions(-) diff --git a/DarkUI/DarkUI/Docking/DarkDockPanel.cs b/DarkUI/DarkUI/Docking/DarkDockPanel.cs index 3717a2185..aa4627bf5 100644 --- a/DarkUI/DarkUI/Docking/DarkDockPanel.cs +++ b/DarkUI/DarkUI/Docking/DarkDockPanel.cs @@ -1,4 +1,4 @@ -using DarkUI.Config; +using DarkUI.Config; using DarkUI.Win32; using System; using System.Collections.Generic; @@ -28,6 +28,7 @@ public class DarkDockPanel : UserControl private bool _prioritizeRight = true; private DarkDockContent _activeContent; private bool _switchingContent; + private bool _isBulkUpdating; #endregion @@ -148,6 +149,8 @@ public Dictionary Regions #region Constructor Region + internal bool IsBulkUpdating => _isBulkUpdating; + public DarkDockPanel() { Splitters = new List(); @@ -188,9 +191,11 @@ public void AddContent(DarkDockContent dockContent, DarkDockGroup dockGroup) var region = _regions[dockContent.DockArea]; region.AddContent(dockContent, dockGroup); - ContentAdded?.Invoke(this, new DockContentEventArgs(dockContent)); - - dockContent.Select(); + if (!_isBulkUpdating) + { + ContentAdded?.Invoke(this, new DockContentEventArgs(dockContent)); + dockContent.Select(); + } } public void InsertContent(DarkDockContent dockContent, DarkDockGroup dockGroup, DockInsertType insertType) @@ -216,8 +221,12 @@ public void RemoveContent() if (_contents.Count == 0) return; - while(_contents.Count > 0) + BeginBulkUpdate(); + + while (_contents.Count > 0) RemoveContent(_contents.First()); + + EndBulkUpdate(); } public void RemoveContent(DarkDockContent dockContent) @@ -231,7 +240,8 @@ public void RemoveContent(DarkDockContent dockContent) var region = _regions[dockContent.DockArea]; region.RemoveContent(dockContent); - ContentRemoved?.Invoke(this, new DockContentEventArgs(dockContent)); + if (!_isBulkUpdating) + ContentRemoved?.Invoke(this, new DockContentEventArgs(dockContent)); } public bool ContainsContent(DarkDockContent dockContent) @@ -353,7 +363,7 @@ public DockPanelState GetDockPanelState() public void RestoreDockPanelState(DockPanelState state, Func getContentBySerializationKey) { - SuspendLayout(); + BeginBulkUpdate(); foreach (var region in state.Regions.OrderByDescending(r => r.Area)) { @@ -408,7 +418,39 @@ public void RestoreDockPanelState(DockPanelState state, Func GetContents() @@ -459,6 +465,12 @@ private void RemoveSplitter() DockPanel.Splitters.Remove(_splitter); } + internal void FinalizeLayout() + { + RebuildGroupSplitters(); + PositionGroups(); + } + private void RebuildGroupSplitters() { if (DockArea == DarkDockArea.Document) From 0c09eec8e77a515532a235fe7ba736c2dd9d5f11 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 12:48:59 +0200 Subject: [PATCH 15/18] Move bottom panel visibility settings into window layout class --- TombEditor/Command.cs | 4 ++-- TombEditor/Configuration.cs | 10 ++++++---- TombEditor/Editor.cs | 2 +- TombEditor/Forms/FormMain.cs | 14 ++++++++++---- TombEditor/ToolWindows/MainView.cs | 12 ++++++------ 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs index 75fd0aaae..20a0cd76f 100644 --- a/TombEditor/Command.cs +++ b/TombEditor/Command.cs @@ -937,7 +937,7 @@ static CommandHandler() AddCommand("ShowFlybyTimeline", "Flyby timeline", CommandType.Windows, delegate (CommandArgs args) { - args.Editor.Configuration.UI_ShowFlybyTimeline = !args.Editor.Configuration.UI_ShowFlybyTimeline; + args.Editor.Configuration.Window_Layout.ShowFlybyTimeline = !args.Editor.Configuration.Window_Layout.ShowFlybyTimeline; args.Editor.ConfigurationChange(); }); @@ -1730,7 +1730,7 @@ static CommandHandler() AddCommand("ShowStatistics", "Statistics display", CommandType.Windows, delegate (CommandArgs args) { - args.Editor.Configuration.UI_ShowStats = !args.Editor.Configuration.UI_ShowStats; + args.Editor.Configuration.Window_Layout.ShowStats = !args.Editor.Configuration.Window_Layout.ShowStats; args.Editor.ConfigurationChange(); }); diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index 466d3fb4e..94cd7d784 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -187,8 +187,6 @@ public class Configuration : ConfigurationBase // User interface options - public bool UI_ShowStats { get; set; } = true; - public bool UI_ShowFlybyTimeline { get; set; } = true; public bool UI_AutoFillTriggerTypesForSwitchAndKey { get; set; } = true; public bool UI_AutoSwitchRoomToOutsideOnAppliedInvisibleTexture { get; set; } = false; public bool UI_DiscardSelectionOnModeSwitch { get; set; } = false; @@ -428,7 +426,9 @@ public class NamedLayout : ICloneable public DockPanelState State { get; set; } = Configuration.Window_LayoutDefault; public Point ToolboxPosition { get; set; } = new Point(15, 15); public Point ObjectBrushToolboxPosition { get; set; } = new Point(50, 15); - public bool ToolboxVisible { get; set; } = true; + public bool ShowToolbox { get; set; } = true; + public bool ShowStats { get; set; } = true; + public bool ShowFlybyTimeline { get; set; } = true; public NamedLayout Clone() => new NamedLayout { @@ -436,7 +436,9 @@ public class NamedLayout : ICloneable State = State.Clone(), ToolboxPosition = ToolboxPosition, ObjectBrushToolboxPosition = ObjectBrushToolboxPosition, - ToolboxVisible = ToolboxVisible + ShowToolbox = ShowToolbox, + ShowStats = ShowStats, + ShowFlybyTimeline = ShowFlybyTimeline }; object ICloneable.Clone() => Clone(); diff --git a/TombEditor/Editor.cs b/TombEditor/Editor.cs index 7ee822b80..b15166609 100644 --- a/TombEditor/Editor.cs +++ b/TombEditor/Editor.cs @@ -1264,7 +1264,7 @@ private void UpdateLevelStatistics(bool onlyRoom, bool resetCompilationStats = f { // Don't update stats if option is unset or there is no level - if (!Configuration.UI_ShowStats || Level == null) + if (!Configuration.Window_Layout.ShowStats || Level == null) return; // Don't update stats if already updating diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 62832adc0..436c8c84d 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -373,8 +373,8 @@ private void UpdateControls() { ShowRealTintForObjectsToolStripMenuItem.Checked = _editor.Configuration.Rendering3D_ShowRealTintForObjects; drawWhiteTextureLightingOnlyToolStripMenuItem.Checked = _editor.Configuration.Rendering3D_ShowLightingWhiteTextureOnly; - statisticsToolStripMenuItem.Checked = _editor.Configuration.UI_ShowStats; - flybyTimelineToolStripMenuItem.Checked = _editor.Configuration.UI_ShowFlybyTimeline; + statisticsToolStripMenuItem.Checked = _editor.Configuration.Window_Layout.ShowStats; + flybyTimelineToolStripMenuItem.Checked = _editor.Configuration.Window_Layout.ShowFlybyTimeline; } private void RefreshRecentProjectsList() @@ -514,9 +514,13 @@ private void LoadWindowLayout(Configuration configuration) dockArea.RemoveContent(); dockArea.RestoreDockPanelState(configuration.Window_Layout.State, GetWindow); - floatingToolStripMenuItem.Checked = configuration.Window_Layout.ToolboxVisible; + floatingToolStripMenuItem.Checked = configuration.Window_Layout.ShowToolbox; ToolBox.Location = configuration.Window_Layout.ToolboxPosition; ObjectBrushSettings.Location = configuration.Window_Layout.ObjectBrushToolboxPosition; + statisticsToolStripMenuItem.Checked = configuration.Window_Layout.ShowStats; + flybyTimelineToolStripMenuItem.Checked = configuration.Window_Layout.ShowFlybyTimeline; + + ToolWindow_BuildMenu(); } private void SaveWindowLayout(Configuration configuration) @@ -685,8 +689,10 @@ private void Layout_Delete() private void SaveCurrentStateToLayout(NamedLayout target) { target.State = dockArea.GetDockPanelState(); - target.ToolboxVisible = floatingToolStripMenuItem.Checked; + target.ShowToolbox = floatingToolStripMenuItem.Checked; target.ToolboxPosition = ToolBox.Location; + target.ShowStats = statisticsToolStripMenuItem.Checked; + target.ShowFlybyTimeline = flybyTimelineToolStripMenuItem.Checked; if (ObjectBrushSettings.Parent != null) target.ObjectBrushToolboxPosition = ObjectBrushSettings.Location; } diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index 34df2f262..a02630300 100644 --- a/TombEditor/ToolWindows/MainView.cs +++ b/TombEditor/ToolWindows/MainView.cs @@ -50,7 +50,7 @@ public void InitializeRendering(RenderingDevice device) public void AddToolbox(DarkFloatingToolbox toolbox) { - if(!panel3D.Contains(toolbox)) + if (!panel3D.Contains(toolbox)) panel3D.Controls.Add(toolbox); } @@ -278,8 +278,8 @@ _editor.SelectedObject is SinkInstance || private void UpdateBottomPanelVisibility(Configuration settings) { - bool bottomPanelVisible = settings.UI_ShowStats || _editor.IsPreciseGeometryAllowed; - bool timelinePanelVisible = settings.UI_ShowFlybyTimeline; + bool bottomPanelVisible = settings.Window_Layout.ShowStats || _editor.IsPreciseGeometryAllowed; + bool timelinePanelVisible = settings.Window_Layout.ShowFlybyTimeline; UpdateStatistics(); @@ -287,9 +287,9 @@ private void UpdateBottomPanelVisibility(Configuration settings) panelBottomStatus.Visible = bottomPanelVisible; panelStepHeightOptions.Visible = _editor.IsPreciseGeometryAllowed; - tbStats.Visible = settings.UI_ShowStats; + tbStats.Visible = settings.Window_Layout.ShowStats; - panelFlybyTimeline.Visible = settings.UI_ShowFlybyTimeline; + panelFlybyTimeline.Visible = settings.Window_Layout.ShowFlybyTimeline; } private void UpdateToolStripLayout() @@ -395,7 +395,7 @@ private void panel2DMap_DragDrop(object sender, DragEventArgs e) private void UpdateStatistics() { - if (_editor == null || _editor.Level == null || !_editor.Configuration.UI_ShowStats) + if (_editor == null || _editor.Level == null || !_editor.Configuration.Window_Layout.ShowStats) return; var summary = _editor.Stats; From 48e861cb0460094ac7792d8388493b85cbd7b49c Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:58:07 +0200 Subject: [PATCH 16/18] Manage layout switching only through events --- TombEditor/Editor.cs | 5 +++-- TombEditor/Forms/FormMain.cs | 9 ++++++--- TombEditor/ToolWindows/MainView.cs | 5 +++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/TombEditor/Editor.cs b/TombEditor/Editor.cs index b15166609..ff8065922 100644 --- a/TombEditor/Editor.cs +++ b/TombEditor/Editor.cs @@ -849,8 +849,9 @@ public void ToggleToolWindow(Type contentType) RaiseEvent(new ToolWindowToggleEvent() { ContentType = contentType }); } - // Layout switch event - public class SwitchLayoutEvent : IEditorEvent + // Layout switch events + public class LayoutSwitchedEvent : IEditorEvent { } + public class SwitchLayoutEvent : IEditorEvent { public int LayoutIndex { get; internal set; } } diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 436c8c84d..9f9dc64cf 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -341,6 +341,8 @@ obj is Editor.SelectedRoomChangedEvent || var layouts = _editor.Configuration.Window_CustomLayouts; if (layoutEvent.LayoutIndex >= 0 && layoutEvent.LayoutIndex < layouts.Count) Layout_SwitchTo(layouts[layoutEvent.LayoutIndex].Name); + else + Layout_RestoreDefault(); } if (obj is Editor.LevelFileNameChangedEvent) @@ -521,6 +523,7 @@ private void LoadWindowLayout(Configuration configuration) flybyTimelineToolStripMenuItem.Checked = configuration.Window_Layout.ShowFlybyTimeline; ToolWindow_BuildMenu(); + _editor.RaiseEvent(new Editor.LayoutSwitchedEvent()); } private void SaveWindowLayout(Configuration configuration) @@ -588,7 +591,7 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e // Default layout entry. var defaultItem = new ToolStripMenuItem("Default"); - defaultItem.Click += (s, ev) => Layout_RestoreDefault(); + defaultItem.Click += (s, ev) => _editor.SwitchLayout(-1); layoutsToolStripMenuItem.DropDownItems.Add(defaultItem); // Custom layout entries. @@ -609,8 +612,8 @@ private void layoutsToolStripMenuItem_DropDownOpening(object sender, EventArgs e item.ShortcutKeyDisplayString = string.Join(", ", config.UI_Hotkeys[hotkeyName].Select(h => h.ToString()).Where(str => !string.IsNullOrWhiteSpace(str))); } - string layoutName = layout.Name; - item.Click += (s, ev) => Layout_SwitchTo(layoutName); + int layoutIndex = i; + item.Click += (s, ev) => _editor.SwitchLayout(layoutIndex); layoutsToolStripMenuItem.DropDownItems.Add(item); } } diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index a02630300..0b1961183 100644 --- a/TombEditor/ToolWindows/MainView.cs +++ b/TombEditor/ToolWindows/MainView.cs @@ -178,9 +178,10 @@ private void EditorEventRaised(IEditorEvent obj) // Dismiss any messages if (obj is Editor.LevelChangedEvent) - { popup.Hide(); - } + + if (obj is Editor.LayoutSwitchedEvent) + RefreshControls(_editor.Configuration); // Update version-specific controls if (obj is Editor.InitEvent || From c6e881e7adab7ca9157282c51c6817d060370e4f Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 20:19:21 +0200 Subject: [PATCH 17/18] Address Copilot comments --- DarkUI/DarkUI/Docking/DarkDockPanel.cs | 5 ++--- TombEditor/Configuration.cs | 4 ++-- TombEditor/Editor.cs | 6 +++--- TombEditor/Forms/FormMain.cs | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/DarkUI/DarkUI/Docking/DarkDockPanel.cs b/DarkUI/DarkUI/Docking/DarkDockPanel.cs index aa4627bf5..2f20bd88f 100644 --- a/DarkUI/DarkUI/Docking/DarkDockPanel.cs +++ b/DarkUI/DarkUI/Docking/DarkDockPanel.cs @@ -191,11 +191,10 @@ public void AddContent(DarkDockContent dockContent, DarkDockGroup dockGroup) var region = _regions[dockContent.DockArea]; region.AddContent(dockContent, dockGroup); + ContentAdded?.Invoke(this, new DockContentEventArgs(dockContent)); + if (!_isBulkUpdating) - { - ContentAdded?.Invoke(this, new DockContentEventArgs(dockContent)); dockContent.Select(); - } } public void InsertContent(DarkDockContent dockContent, DarkDockGroup dockGroup, DockInsertType insertType) diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index 94cd7d784..a69250404 100644 --- a/TombEditor/Configuration.cs +++ b/TombEditor/Configuration.cs @@ -296,7 +296,7 @@ public class Configuration : ConfigurationBase public Size Window_FormMaterialEditor_Size { get; set; } = new Size(537, 560); public bool Window_FormMaterialEditor_Maximized { get; set; } = false; - public NamedLayout Window_Layout { get; set; } = new NamedLayout { State = Window_LayoutDefault }; + public NamedLayout Window_Layout { get; set; } = new NamedLayout { State = Window_LayoutDefault.Clone() }; public List Window_CustomLayouts { get; set; } = new List(); public string Window_ActiveLayoutName { get; set; } = string.Empty; @@ -423,7 +423,7 @@ public void EnsureDefaults() public class NamedLayout : ICloneable { public string Name { get; set; } = string.Empty; - public DockPanelState State { get; set; } = Configuration.Window_LayoutDefault; + public DockPanelState State { get; set; } = Configuration.Window_LayoutDefault.Clone(); public Point ToolboxPosition { get; set; } = new Point(15, 15); public Point ObjectBrushToolboxPosition { get; set; } = new Point(50, 15); public bool ShowToolbox { get; set; } = true; diff --git a/TombEditor/Editor.cs b/TombEditor/Editor.cs index ff8065922..b07272891 100644 --- a/TombEditor/Editor.cs +++ b/TombEditor/Editor.cs @@ -849,9 +849,9 @@ public void ToggleToolWindow(Type contentType) RaiseEvent(new ToolWindowToggleEvent() { ContentType = contentType }); } - // Layout switch events - public class LayoutSwitchedEvent : IEditorEvent { } - public class SwitchLayoutEvent : IEditorEvent + // Layout switch events + public class LayoutSwitchedEvent : IEditorEvent { } + public class SwitchLayoutEvent : IEditorEvent { public int LayoutIndex { get; internal set; } } diff --git a/TombEditor/Forms/FormMain.cs b/TombEditor/Forms/FormMain.cs index 9f9dc64cf..43cb41132 100644 --- a/TombEditor/Forms/FormMain.cs +++ b/TombEditor/Forms/FormMain.cs @@ -341,7 +341,7 @@ obj is Editor.SelectedRoomChangedEvent || var layouts = _editor.Configuration.Window_CustomLayouts; if (layoutEvent.LayoutIndex >= 0 && layoutEvent.LayoutIndex < layouts.Count) Layout_SwitchTo(layouts[layoutEvent.LayoutIndex].Name); - else + else if (layoutEvent.LayoutIndex <= -1) Layout_RestoreDefault(); } From 3afe381a640db895354d7308958d41123fa64b89 Mon Sep 17 00:00:00 2001 From: Lwmte <3331699+Lwmte@users.noreply.github.com> Date: Sat, 18 Apr 2026 20:32:04 +0200 Subject: [PATCH 18/18] Always invoke ContentRemoved during bulk updates as it doesn't affect the performance --- DarkUI/DarkUI/Docking/DarkDockPanel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/DarkUI/DarkUI/Docking/DarkDockPanel.cs b/DarkUI/DarkUI/Docking/DarkDockPanel.cs index 2f20bd88f..2764dd876 100644 --- a/DarkUI/DarkUI/Docking/DarkDockPanel.cs +++ b/DarkUI/DarkUI/Docking/DarkDockPanel.cs @@ -239,8 +239,7 @@ public void RemoveContent(DarkDockContent dockContent) var region = _regions[dockContent.DockArea]; region.RemoveContent(dockContent); - if (!_isBulkUpdating) - ContentRemoved?.Invoke(this, new DockContentEventArgs(dockContent)); + ContentRemoved?.Invoke(this, new DockContentEventArgs(dockContent)); } public bool ContainsContent(DarkDockContent dockContent)