From 570c3f7462f94f6967ac575535ab8ea7e2fbfc47 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 11 Feb 2025 16:39:15 +0100 Subject: [PATCH 1/4] add option for single tab tab header bar visibility --- Source/Editor/GUI/Docking/DockPanelProxy.cs | 11 +++-- Source/Editor/Options/InterfaceOptions.cs | 55 ++++++++++++--------- 2 files changed, 38 insertions(+), 28 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index bc38e2953..ad25d6c85 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -14,11 +14,11 @@ namespace FlaxEditor.GUI.Docking { private DockPanel _panel; private double _dragEnterTime = -1; - #if PLATFORM_WINDOWS - private const bool HideTabForSingleTab = true; - #else - private const bool HideTabForSingleTab = false; - #endif +#if PLATFORM_WINDOWS + private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars; +#else + private readonly bool _hideTabForSingleTab = false; +#endif /// /// The is mouse down flag (left button). @@ -57,6 +57,7 @@ namespace FlaxEditor.GUI.Docking private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, DockPanel.DefaultHeaderHeight); private bool IsSingleFloatingWindow => HideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0; + private bool IsSingleFloatingWindow => _hideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0; /// /// Initializes a new instance of the class. diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 4cd76adc6..a6189e34c 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -167,15 +167,6 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(10), Tooltip("Editor User Interface scale. Applied to all UI elements, windows and text. Can be used to scale the interface up on a bigger display. Editor restart required.")] public float InterfaceScale { get; set; } = 1.0f; -#if PLATFORM_WINDOWS - /// - /// Gets or sets a value indicating whether use native window title bar. Editor restart required. - /// - [DefaultValue(false)] - [EditorDisplay("Interface"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")] - public bool UseNativeWindowSystem { get; set; } = false; -#endif - /// /// Gets or sets a value indicating whether show selected camera preview in the editor window. /// @@ -183,20 +174,6 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(80), Tooltip("Determines whether show selected camera preview in the edit window.")] public bool ShowSelectedCameraPreview { get; set; } = true; - /// - /// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor. - /// - [DefaultValue(false)] - [EditorDisplay("Interface", "Center Mouse On Game Window Focus"), EditorOrder(100), Tooltip("Determines whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor.")] - public bool CenterMouseOnGameWinFocus { get; set; } = false; - - /// - /// Gets or sets the method window opening. - /// - [DefaultValue(DockStateProxy.Float)] - [EditorDisplay("Interface", "New Window Location"), EditorOrder(150), Tooltip("Define the opening method for new windows, open in a new tab by default.")] - public DockStateProxy NewWindowLocation { get; set; } = DockStateProxy.Float; - /// /// Gets or sets the editor icons scale. Editor restart required. /// @@ -265,6 +242,38 @@ namespace FlaxEditor.Options [EditorDisplay("Interface"), EditorOrder(322)] public bool ScrollToScriptOnAdd { get; set; } = true; +#if PLATFORM_WINDOWS + /// + /// Gets or sets a value indicating whether use native window title bar. Editor restart required. + /// + [DefaultValue(false)] + [EditorDisplay("Tabs & Windows"), EditorOrder(70), Tooltip("Determines whether use native window title bar. Editor restart required.")] + public bool UseNativeWindowSystem { get; set; } = false; +#endif + +#if PLATFORM_WINDOWS + /// + /// Gets or sets a value indicating whether a window containing a single tabs hides the tab bar. Editor restart recommended. + /// + [DefaultValue(true)] + [EditorDisplay("Tabs & Windows", "Hide Single-Tab Window Tab Bars"), EditorOrder(71)] + public bool HideSingleTabWindowTabBars { get; set; } = true; +#endif + + /// + /// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor. + /// + [DefaultValue(false)] + [EditorDisplay("Tabs & Windows", "Center Mouse On Game Window Focus"), EditorOrder(101), Tooltip("Determines whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor.")] + public bool CenterMouseOnGameWinFocus { get; set; } = false; + + /// + /// Gets or sets the method window opening. + /// + [DefaultValue(DockStateProxy.Float)] + [EditorDisplay("Tabs & Windows", "New Window Location"), EditorOrder(150), Tooltip("Define the opening method for new windows, open in a new tab by default.")] + public DockStateProxy NewWindowLocation { get; set; } = DockStateProxy.Float; + /// /// Gets or sets the timestamps prefix mode for output log messages. /// From 409703d675b0bca4d680498b5972529cc684850c Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 11 Feb 2025 16:39:33 +0100 Subject: [PATCH 2/4] add editor option for tab height --- Source/Editor/GUI/Docking/DockHintWindow.cs | 4 +-- Source/Editor/GUI/Docking/DockPanelProxy.cs | 32 ++++++++++----------- Source/Editor/Options/InterfaceOptions.cs | 7 +++++ 3 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockHintWindow.cs b/Source/Editor/GUI/Docking/DockHintWindow.cs index 2b0902cf9..10b204345 100644 --- a/Source/Editor/GUI/Docking/DockHintWindow.cs +++ b/Source/Editor/GUI/Docking/DockHintWindow.cs @@ -215,8 +215,8 @@ namespace FlaxEditor.GUI.Docking switch (state) { case DockState.DockFill: - result.Location.Y += DockPanel.DefaultHeaderHeight; - result.Size.Y -= DockPanel.DefaultHeaderHeight; + result.Location.Y += Editor.Instance.Options.Options.Interface.TabHeight; + result.Size.Y -= Editor.Instance.Options.Options.Interface.TabHeight; break; case DockState.DockTop: result.Size.Y *= DockPanel.DefaultSplitterValue; diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index ad25d6c85..23ba6c577 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -1,6 +1,7 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. using FlaxEditor.GUI.ContextMenu; +using FlaxEditor.Options; using FlaxEngine; using FlaxEngine.GUI; @@ -14,6 +15,7 @@ namespace FlaxEditor.GUI.Docking { private DockPanel _panel; private double _dragEnterTime = -1; + private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight; #if PLATFORM_WINDOWS private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars; #else @@ -54,9 +56,7 @@ namespace FlaxEditor.GUI.Docking /// The start drag asynchronous window. /// public DockWindow StartDragAsyncWindow; - - private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, DockPanel.DefaultHeaderHeight); - private bool IsSingleFloatingWindow => HideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0; + private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, _tabHeight); private bool IsSingleFloatingWindow => _hideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0; /// @@ -81,7 +81,7 @@ namespace FlaxEditor.GUI.Docking var tabsCount = _panel.TabsCount; if (tabsCount == 1) { - var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); + var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); if (HeaderRectangle.Contains(position)) { closeButton = crossRect.Contains(position); @@ -97,11 +97,11 @@ namespace FlaxEditor.GUI.Docking var titleSize = tab.TitleSize; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; - var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight); + var tabRect = new Rectangle(x, 0, width, HeaderRectangle.Height); var isMouseOver = tabRect.Contains(position); if (isMouseOver) { - var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); + var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); closeButton = crossRect.Contains(position); result = tab; break; @@ -133,7 +133,7 @@ namespace FlaxEditor.GUI.Docking float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin; if (tab == win) { - bounds = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight); + bounds = new Rectangle(x, 0, width, HeaderRectangle.Height); return; } x += width; @@ -213,7 +213,7 @@ namespace FlaxEditor.GUI.Docking { Render2D.DrawSprite( tab.Icon, - new Rectangle(DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize), + new Rectangle(DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize), style.Foreground); } @@ -222,13 +222,13 @@ namespace FlaxEditor.GUI.Docking Render2D.DrawText( style.FontMedium, tab.Title, - new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, DockPanel.DefaultHeaderHeight), + new Rectangle(DockPanel.DefaultLeftTextMargin + iconWidth, 0, Width - DockPanel.DefaultLeftTextMargin - DockPanel.DefaultButtonsSize - 2 * DockPanel.DefaultButtonsMargin, HeaderRectangle.Height), style.Foreground, TextAlignment.Near, TextAlignment.Center); // Draw cross - var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); + var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition); if (isMouseOverCross) Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f); @@ -249,7 +249,7 @@ namespace FlaxEditor.GUI.Docking var titleSize = tab.TitleSize; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; - var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight); + var tabRect = new Rectangle(x, 0, width, headerRect.Height); var isMouseOver = tabRect.Contains(MousePosition); var isSelected = _panel.SelectedTab == tab; @@ -276,7 +276,7 @@ namespace FlaxEditor.GUI.Docking { Render2D.DrawSprite( tab.Icon, - new Rectangle(x + DockPanel.DefaultLeftTextMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize), + new Rectangle(x + DockPanel.DefaultLeftTextMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize), style.Foreground); } @@ -285,7 +285,7 @@ namespace FlaxEditor.GUI.Docking Render2D.DrawText( style.FontMedium, tab.Title, - new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, DockPanel.DefaultHeaderHeight), + new Rectangle(x + DockPanel.DefaultLeftTextMargin + iconWidth, 0, 10000, HeaderRectangle.Height), style.Foreground, TextAlignment.Near, TextAlignment.Center); @@ -293,7 +293,7 @@ namespace FlaxEditor.GUI.Docking // Draw cross if (isSelected || isMouseOver) { - var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (DockPanel.DefaultHeaderHeight - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); + var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition); if (isMouseOverCross) Render2D.FillRectangle(crossRect, tabColor * 1.3f); @@ -305,7 +305,7 @@ namespace FlaxEditor.GUI.Docking } // Draw selected tab strip - Render2D.FillRectangle(new Rectangle(0, DockPanel.DefaultHeaderHeight - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal); + Render2D.FillRectangle(new Rectangle(0, HeaderRectangle.Height - 2, Width, 2), containsFocus ? style.BackgroundSelected : style.BackgroundNormal); } } @@ -523,7 +523,7 @@ namespace FlaxEditor.GUI.Docking if (IsSingleFloatingWindow) rect = new Rectangle(0, 0, Width, Height); else - rect = new Rectangle(0, DockPanel.DefaultHeaderHeight, Width, Height - DockPanel.DefaultHeaderHeight); + rect = new Rectangle(0, HeaderRectangle.Height, Width, Height - HeaderRectangle.Height); } private DragDropEffect TrySelectTabUnderLocation(ref Float2 location) diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index a6189e34c..925fb8c60 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -260,6 +260,13 @@ namespace FlaxEditor.Options public bool HideSingleTabWindowTabBars { get; set; } = true; #endif + /// + /// Gets or sets a value indicating the height of window tabs. Editor restart required. + /// + [DefaultValue(20.0f), Limit(15.0f, 40.0f)] + [EditorDisplay("Tabs & Windows"), EditorOrder(100)] + public float TabHeight { get; set; } = 20.0f; + /// /// Gets or sets a value indicating whether center mouse position on window focus in play mode. Helps when working with games that lock mouse cursor. /// From ba35123420710ff6ad9b6b665d8e28fc7aaf80b9 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Tue, 11 Feb 2025 18:15:46 +0100 Subject: [PATCH 3/4] add minimum tab width --- Source/Editor/GUI/Docking/DockPanelProxy.cs | 12 ++++++++++++ Source/Editor/Options/InterfaceOptions.cs | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index 23ba6c577..ef6c2cb1a 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -16,6 +16,8 @@ namespace FlaxEditor.GUI.Docking private DockPanel _panel; private double _dragEnterTime = -1; private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight; + private bool _useMinimumTabWidth = Editor.Instance.Options.Options.Interface.UseMinimumTabWidth; + private float _minimumTabWidth = Editor.Instance.Options.Options.Interface.MinimumTabWidth; #if PLATFORM_WINDOWS private readonly bool _hideTabForSingleTab = Editor.Instance.Options.Options.Interface.HideSingleTabWindowTabBars; #else @@ -56,6 +58,7 @@ namespace FlaxEditor.GUI.Docking /// The start drag asynchronous window. /// public DockWindow StartDragAsyncWindow; + private Rectangle HeaderRectangle => new Rectangle(0, 0, Width, _tabHeight); private bool IsSingleFloatingWindow => _hideTabForSingleTab && _panel.TabsCount == 1 && _panel.IsFloating && _panel.ChildPanelsCount == 0; @@ -97,6 +100,10 @@ namespace FlaxEditor.GUI.Docking var titleSize = tab.TitleSize; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; + + if (_useMinimumTabWidth && width < _minimumTabWidth) + width = _minimumTabWidth; + var tabRect = new Rectangle(x, 0, width, HeaderRectangle.Height); var isMouseOver = tabRect.Contains(position); if (isMouseOver) @@ -248,7 +255,12 @@ namespace FlaxEditor.GUI.Docking var tabColor = Color.Black; var titleSize = tab.TitleSize; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; + var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; + + if (_useMinimumTabWidth && width < _minimumTabWidth) + width = _minimumTabWidth; + var tabRect = new Rectangle(x, 0, width, headerRect.Height); var isMouseOver = tabRect.Contains(MousePosition); var isSelected = _panel.SelectedTab == tab; diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 925fb8c60..1e58b2a23 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -260,6 +260,20 @@ namespace FlaxEditor.Options public bool HideSingleTabWindowTabBars { get; set; } = true; #endif + /// + /// Gets or sets a value indicating wether the minum tab width should be used. Editor restart required. + /// + [DefaultValue(false)] + [EditorDisplay("Tabs & Windows"), EditorOrder(99)] + public bool UseMinimumTabWidth { get; set; } = false; + + /// + /// Gets or sets a value indicating the minimum tab width. If a tab is smaller than this width, its width will be set to this. Editor restart required. + /// + [DefaultValue(80.0f), Limit(50.0f, 150.0f)] + [EditorDisplay("Tabs & Windows"), EditorOrder(99), VisibleIf(nameof(UseMinimumTabWidth))] + public float MinimumTabWidth { get; set; } = 80.0f; + /// /// Gets or sets a value indicating the height of window tabs. Editor restart required. /// From 391c67b1a94895e4909fb4680f13023337b7d277 Mon Sep 17 00:00:00 2001 From: xxSeys1 Date: Sun, 23 Mar 2025 14:09:59 +0100 Subject: [PATCH 4/4] add visibility options for the tab close button --- Source/Editor/GUI/Docking/DockPanelProxy.cs | 61 +++++++++++++++------ Source/Editor/Options/InterfaceOptions.cs | 26 +++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/Source/Editor/GUI/Docking/DockPanelProxy.cs b/Source/Editor/GUI/Docking/DockPanelProxy.cs index ef6c2cb1a..983497c59 100644 --- a/Source/Editor/GUI/Docking/DockPanelProxy.cs +++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs @@ -14,6 +14,7 @@ namespace FlaxEditor.GUI.Docking public class DockPanelProxy : ContainerControl { private DockPanel _panel; + private InterfaceOptions.TabCloseButtonVisibility closeButtonVisibility; private double _dragEnterTime = -1; private float _tabHeight = Editor.Instance.Options.Options.Interface.TabHeight; private bool _useMinimumTabWidth = Editor.Instance.Options.Options.Interface.UseMinimumTabWidth; @@ -74,6 +75,14 @@ namespace FlaxEditor.GUI.Docking _panel = panel; AnchorPreset = AnchorPresets.StretchAll; Offsets = Margin.Zero; + + Editor.Instance.Options.OptionsChanged += OnEditorOptionsChanged; + OnEditorOptionsChanged(Editor.Instance.Options.Options); + } + + private void OnEditorOptionsChanged(EditorOptions options) + { + closeButtonVisibility = options.Interface.ShowTabCloseButton; } private DockWindow GetTabAtPos(Float2 position, out bool closeButton) @@ -87,8 +96,8 @@ namespace FlaxEditor.GUI.Docking var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); if (HeaderRectangle.Contains(position)) { - closeButton = crossRect.Contains(position); result = _panel.GetTab(0); + closeButton = crossRect.Contains(position) && IsCloseButtonVisible(result, closeButtonVisibility); } } else @@ -97,9 +106,7 @@ namespace FlaxEditor.GUI.Docking for (int i = 0; i < tabsCount; i++) { var tab = _panel.GetTab(i); - var titleSize = tab.TitleSize; - var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; - var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; + float width = CalculateTabWidth(tab, closeButtonVisibility); if (_useMinimumTabWidth && width < _minimumTabWidth) width = _minimumTabWidth; @@ -109,7 +116,7 @@ namespace FlaxEditor.GUI.Docking if (isMouseOver) { var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); - closeButton = crossRect.Contains(position); + closeButton = crossRect.Contains(position) && IsCloseButtonVisible(tab, closeButtonVisibility); result = tab; break; } @@ -120,6 +127,24 @@ namespace FlaxEditor.GUI.Docking return result; } + private bool IsCloseButtonVisible(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode) + { + return visibilityMode != InterfaceOptions.TabCloseButtonVisibility.Never && + (visibilityMode == InterfaceOptions.TabCloseButtonVisibility.Always || + (visibilityMode == InterfaceOptions.TabCloseButtonVisibility.SelectedTab && _panel.SelectedTab == win)); + } + + private float CalculateTabWidth(DockWindow win, InterfaceOptions.TabCloseButtonVisibility visibilityMode) + { + var iconWidth = win.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; + var width = win.TitleSize.X + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; + + if (IsCloseButtonVisible(win, visibilityMode)) + width += 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultButtonsSize; + + return width; + } + private void GetTabRect(DockWindow win, out Rectangle bounds) { FlaxEngine.Assertions.Assert.IsTrue(_panel.ContainsTab(win)); @@ -137,7 +162,7 @@ namespace FlaxEditor.GUI.Docking { var tab = _panel.GetTab(i); var titleSize = tab.TitleSize; - float width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin; + float width = CalculateTabWidth(tab, closeButtonVisibility); if (tab == win) { bounds = new Rectangle(x, 0, width, HeaderRectangle.Height); @@ -234,12 +259,15 @@ namespace FlaxEditor.GUI.Docking TextAlignment.Near, TextAlignment.Center); - // Draw cross - var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); - bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition); - if (isMouseOverCross) - Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f); - Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey); + if (IsCloseButtonVisible(tab, closeButtonVisibility)) + { + // Draw cross + var crossRect = new Rectangle(Width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); + bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition); + if (isMouseOverCross) + Render2D.FillRectangle(crossRect, (containsFocus ? style.BackgroundSelected : style.LightBackground) * 1.3f); + Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey); + } } else { @@ -253,10 +281,9 @@ namespace FlaxEditor.GUI.Docking // Cache data var tab = _panel.GetTab(i); var tabColor = Color.Black; - var titleSize = tab.TitleSize; var iconWidth = tab.Icon.IsValid ? DockPanel.DefaultButtonsSize + DockPanel.DefaultLeftTextMargin : 0; - - var width = titleSize.X + DockPanel.DefaultButtonsSize + 2 * DockPanel.DefaultButtonsMargin + DockPanel.DefaultLeftTextMargin + DockPanel.DefaultRightTextMargin + iconWidth; + + float width = CalculateTabWidth(tab, closeButtonVisibility); if (_useMinimumTabWidth && width < _minimumTabWidth) width = _minimumTabWidth; @@ -303,7 +330,7 @@ namespace FlaxEditor.GUI.Docking TextAlignment.Center); // Draw cross - if (isSelected || isMouseOver) + if (IsCloseButtonVisible(tab, closeButtonVisibility)) { var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize); bool isMouseOverCross = isMouseOver && crossRect.Contains(MousePosition); @@ -312,7 +339,7 @@ namespace FlaxEditor.GUI.Docking Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey); } - // Move + // Set the start position for the next tab x += width; } diff --git a/Source/Editor/Options/InterfaceOptions.cs b/Source/Editor/Options/InterfaceOptions.cs index 1e58b2a23..06679f24b 100644 --- a/Source/Editor/Options/InterfaceOptions.cs +++ b/Source/Editor/Options/InterfaceOptions.cs @@ -76,6 +76,25 @@ namespace FlaxEditor.Options DockBottom = DockState.DockBottom } + /// + /// Options for the visibility status of the tab close button. + /// + public enum TabCloseButtonVisibility + { + /// + /// Never show the close button. + /// + Never, + /// + /// Show the close button on tabs that are currently selected. + /// + SelectedTab, + /// + /// Show the close button on all tabs that can be closed. + /// + Always + } + /// /// Options for the action taken by the play button. /// @@ -295,6 +314,13 @@ namespace FlaxEditor.Options [EditorDisplay("Tabs & Windows", "New Window Location"), EditorOrder(150), Tooltip("Define the opening method for new windows, open in a new tab by default.")] public DockStateProxy NewWindowLocation { get; set; } = DockStateProxy.Float; + /// + /// Gets or sets a value indicating when the tab close button should be visible. + /// + [DefaultValue(TabCloseButtonVisibility.SelectedTab)] + [EditorDisplay("Tabs & Windows"), EditorOrder(151)] + public TabCloseButtonVisibility ShowTabCloseButton { get; set; } = TabCloseButtonVisibility.SelectedTab; + /// /// Gets or sets the timestamps prefix mode for output log messages. ///