diff --git a/DarkUI/DarkUI/Docking/DarkDockPanel.cs b/DarkUI/DarkUI/Docking/DarkDockPanel.cs index 3717a21851..2764dd876a 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(); @@ -190,7 +193,8 @@ public void AddContent(DarkDockContent dockContent, DarkDockGroup dockGroup) ContentAdded?.Invoke(this, new DockContentEventArgs(dockContent)); - dockContent.Select(); + if (!_isBulkUpdating) + dockContent.Select(); } public void InsertContent(DarkDockContent dockContent, DarkDockGroup dockGroup, DockInsertType insertType) @@ -216,8 +220,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) @@ -353,7 +361,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 +416,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) diff --git a/DarkUI/DarkUI/Docking/DockGroupState.cs b/DarkUI/DarkUI/Docking/DockGroupState.cs index bbc88b60a6..cbaafdfbb6 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 a64e452574..68f4f7e797 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 7ec1c1300b..41390d99a8 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/Installer/Changes.txt b/Installer/Changes.txt index 837bf09134..12d8ce5231 100644 --- a/Installer/Changes.txt +++ b/Installer/Changes.txt @@ -7,6 +7,7 @@ Tomb Editor: * Added Flyby Timeline control to edit and preview flyby sequences. * Added "Preview flyby sequence" and "Preview camera" context menus for cameras. * Added On Pickup, On Vehicle Enter and On Vehicle Exit global events in the global event set editor. + * Added multiple window layout support. WadTool: * Added missing TR2 henchman death sound (ID 838) and appropriate TR2 -> TEN conversion. diff --git a/TombEditor/Command.cs b/TombEditor/Command.cs index 05f67389ac..20a0cd76f9 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,10 +1730,17 @@ 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(); }); + for (int i = 0; i < Configuration.MaxWindowLayouts; i++) + { + int currentLayoutIndex = i; + 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) { args.Editor.Configuration.Rendering3D_ShowPortals = !args.Editor.Configuration.Rendering3D_ShowPortals; diff --git a/TombEditor/Configuration.cs b/TombEditor/Configuration.cs index e1f44ed845..a692504042 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; @@ -13,6 +14,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 @@ -61,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; @@ -188,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; @@ -299,7 +296,9 @@ 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.Clone() }; + public List Window_CustomLayouts { get; set; } = new List(); + public string Window_ActiveLayoutName { get; set; } = string.Empty; public void EnsureDefaults() { @@ -420,4 +419,28 @@ public void EnsureDefaults() } }; } + + public class NamedLayout : ICloneable + { + public string Name { get; set; } = string.Empty; + 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; + public bool ShowStats { get; set; } = true; + public bool ShowFlybyTimeline { get; set; } = true; + + public NamedLayout Clone() => new NamedLayout + { + Name = Name, + State = State.Clone(), + ToolboxPosition = ToolboxPosition, + ObjectBrushToolboxPosition = ObjectBrushToolboxPosition, + ShowToolbox = ShowToolbox, + ShowStats = ShowStats, + ShowFlybyTimeline = ShowFlybyTimeline + }; + + object ICloneable.Clone() => Clone(); + } } diff --git a/TombEditor/Controls/Panel2DMap.cs b/TombEditor/Controls/Panel2DMap.cs index e2ff179475..0269da12bf 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 (LicenseManager.UsageMode == LicenseUsageMode.Runtime) + if (Editor.Instance is not null) { _editor = Editor.Instance; _editor.EditorEventRaised += EditorEventRaised; @@ -117,13 +117,14 @@ public Panel2DMap() 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 c7fa5ef65e..52e27a41d0 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; } @@ -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; @@ -188,13 +188,12 @@ public bool DisablePickingForHiddenRooms public Panel3D() { - Application.AddMessageFilter(filter); - _getViewportCamera = () => Camera; - SetStyle(ControlStyles.Selectable | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); - if (LicenseManager.UsageMode == LicenseUsageMode.Runtime) + if (Editor.Instance is not null) { + _getViewportCamera = () => Camera; + _editor = Editor.Instance; _editor.EditorEventRaised += EditorEventRaised; _editor.GetViewportCamera = _getViewportCamera; @@ -209,8 +208,8 @@ public Panel3D() _flyModeTimer.Tick += FlyModeTimer_Tick; _renderingCachedRooms = new Cache(1024, CacheRoom); + Application.AddMessageFilter(_filter); } - } protected override void Dispose(bool disposing) diff --git a/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs b/TombEditor/Controls/Panel3D/Panel3DCameraMovement.cs index 93e9643ebc..0b542cc127 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/Panel3DInit.cs b/TombEditor/Controls/Panel3D/Panel3DInit.cs index a94d59518e..5d76b24112 100644 --- a/TombEditor/Controls/Panel3D/Panel3DInit.cs +++ b/TombEditor/Controls/Panel3D/Panel3DInit.cs @@ -1,12 +1,11 @@ using SharpDX.Toolkit.Graphics; using System.Numerics; -using TombLib.Graphics.Primitives; -using TombLib.Graphics; using TombLib; +using TombLib.Controls; +using TombLib.Graphics; +using TombLib.Graphics.Primitives; using TombLib.LevelData; using TombLib.Rendering; -using TombLib.Controls; -using System; namespace TombEditor.Controls.Panel3D { @@ -42,8 +41,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/Editor.cs b/TombEditor/Editor.cs index 4fde87dcef..b072728917 100644 --- a/TombEditor/Editor.cs +++ b/TombEditor/Editor.cs @@ -849,6 +849,17 @@ public void ToggleToolWindow(Type contentType) RaiseEvent(new ToolWindowToggleEvent() { ContentType = contentType }); } + // Layout switch events + public class LayoutSwitchedEvent : IEditorEvent { } + 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 { @@ -1254,7 +1265,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.Designer.cs b/TombEditor/Forms/FormMain.Designer.cs index d111888662..bd48277ead 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 1ebb35f37f..43cb411329 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); } } @@ -336,6 +336,15 @@ 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 layoutEvent) + { + var layouts = _editor.Configuration.Window_CustomLayouts; + if (layoutEvent.LayoutIndex >= 0 && layoutEvent.LayoutIndex < layouts.Count) + Layout_SwitchTo(layouts[layoutEvent.LayoutIndex].Name); + else if (layoutEvent.LayoutIndex <= -1) + Layout_RestoreDefault(); + } + if (obj is Editor.LevelFileNameChangedEvent) RefreshRecentProjectsList(); @@ -366,8 +375,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() @@ -505,22 +514,22 @@ 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.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; - floatingToolStripMenuItem.Checked = configuration.Rendering3D_ToolboxVisible; - ToolBox.Location = configuration.Rendering3D_ToolboxPosition; - ObjectBrushSettings.Location = configuration.Rendering3D_ObjectBrushToolboxPosition; + ToolWindow_BuildMenu(); + _editor.RaiseEvent(new Editor.LayoutSwitchedEvent()); } 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) @@ -573,9 +582,135 @@ 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; + + // Default layout entry. + var defaultItem = new ToolStripMenuItem("Default"); + defaultItem.Click += (s, ev) => _editor.SwitchLayout(-1); + 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 < Configuration.MaxWindowLayouts) + { + var hotkeyName = "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))); + } + + int layoutIndex = i; + item.Click += (s, ev) => _editor.SwitchLayout(layoutIndex); + layoutsToolStripMenuItem.DropDownItems.Add(item); + } + } + + // Management entries. + layoutsToolStripMenuItem.DropDownItems.Add(new ToolStripSeparator()); + + 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 = !string.IsNullOrEmpty(config.Window_ActiveLayoutName); + deleteItem.Click += (s, ev) => Layout_Delete(); + layoutsToolStripMenuItem.DropDownItems.Add(deleteItem); + } + + private void Layout_RestoreDefault() + { + SaveCurrentStateToActiveLayout(); + _editor.Configuration.Window_ActiveLayoutName = string.Empty; + _editor.Configuration.Window_Layout = new NamedLayout(); + + LoadWindowLayout(_editor.Configuration); + } + + private void Layout_SwitchTo(string name) { - LoadWindowLayout(new Configuration()); + var layout = _editor.Configuration.Window_CustomLayouts.FirstOrDefault(l => l.Name == name); + if (layout == null) + return; + + SaveCurrentStateToActiveLayout(); + _editor.Configuration.Window_ActiveLayoutName = name; + _editor.Configuration.Window_Layout = layout.Clone(); + LoadWindowLayout(_editor.Configuration); + } + + 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 }; + SaveCurrentStateToLayout(newLayout); + + config.Window_CustomLayouts.Add(newLayout); + config.Window_ActiveLayoutName = name; + config.Window_Layout = newLayout.Clone(); + } + } + + 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); + Layout_RestoreDefault(); + } + + private void SaveCurrentStateToLayout(NamedLayout target) + { + target.State = dockArea.GetDockPanelState(); + target.ShowToolbox = floatingToolStripMenuItem.Checked; + target.ToolboxPosition = ToolBox.Location; + target.ShowStats = statisticsToolStripMenuItem.Checked; + target.ShowFlybyTimeline = flybyTimelineToolStripMenuItem.Checked; + 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) diff --git a/TombEditor/ToolWindows/MainView.cs b/TombEditor/ToolWindows/MainView.cs index 80fbc6da72..0b19611833 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); } @@ -106,18 +106,9 @@ 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) { var o = (Editor.ConfigurationChangedEvent)obj; @@ -132,6 +123,7 @@ private void EditorEventRaised(IEditorEvent obj) } RefreshControls(_editor.Configuration); + UpdateBottomPanelVisibility(_editor.Configuration); } // Gray out menu options that do not apply @@ -175,20 +167,21 @@ 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 if (obj is Editor.LevelChangedEvent) - { popup.Hide(); - } + + if (obj is Editor.LayoutSwitchedEvent) + RefreshControls(_editor.Configuration); // Update version-specific controls if (obj is Editor.InitEvent || @@ -200,8 +193,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 +274,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.Window_Layout.ShowStats || _editor.IsPreciseGeometryAllowed; + bool timelinePanelVisible = settings.Window_Layout.ShowFlybyTimeline; + + UpdateStatistics(); + + panelBottom.Visible = timelinePanelVisible || bottomPanelVisible; + panelBottomStatus.Visible = bottomPanelVisible; + + panelStepHeightOptions.Visible = _editor.IsPreciseGeometryAllowed; + tbStats.Visible = settings.Window_Layout.ShowStats; + + panelFlybyTimeline.Visible = settings.Window_Layout.ShowFlybyTimeline; } private void UpdateToolStripLayout() @@ -396,7 +396,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; diff --git a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs index 7c6f596940..ac9321b78f 100644 --- a/TombLib/TombLib.Forms/Controls/RenderingPanel.cs +++ b/TombLib/TombLib.Forms/Controls/RenderingPanel.cs @@ -81,9 +81,11 @@ 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 && ClientSize.Width > 0 && ClientSize.Height > 0) { SwapChain.Resize(new VectorInt2(ClientSize.Width, ClientSize.Height)); + SwapChain.Clear(ClearColor); + SwapChain.Present(); Invalidate(); } }