diff --git a/Source/Editor/GUI/Docking/DockHintWindow.cs b/Source/Editor/GUI/Docking/DockHintWindow.cs
index cca696d9a..30ffd6287 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 dbb4e082e..20c36e808 100644
--- a/Source/Editor/GUI/Docking/DockPanelProxy.cs
+++ b/Source/Editor/GUI/Docking/DockPanelProxy.cs
@@ -1,6 +1,7 @@
// Copyright (c) Wojciech Figat. All rights reserved.
using FlaxEditor.GUI.ContextMenu;
+using FlaxEditor.Options;
using FlaxEngine;
using FlaxEngine.GUI;
@@ -13,12 +14,16 @@ namespace FlaxEditor.GUI.Docking
public class DockPanelProxy : ContainerControl
{
private DockPanel _panel;
+ private InterfaceOptions.TabCloseButtonVisibility closeButtonVisibility;
private double _dragEnterTime = -1;
- #if PLATFORM_WINDOWS
- private const bool HideTabForSingleTab = true;
- #else
- private const bool HideTabForSingleTab = false;
- #endif
+ 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
+ private readonly bool _hideTabForSingleTab = false;
+#endif
///
/// The is mouse down flag (left button).
@@ -55,8 +60,8 @@ namespace FlaxEditor.GUI.Docking
///
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;
///
/// Initializes a new instance of the class.
@@ -70,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)
@@ -80,11 +93,11 @@ 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);
result = _panel.GetTab(0);
+ closeButton = crossRect.Contains(position) && IsCloseButtonVisible(result, closeButtonVisibility);
}
}
else
@@ -93,15 +106,17 @@ 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;
- var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
+ float width = CalculateTabWidth(tab, closeButtonVisibility);
+
+ if (_useMinimumTabWidth && width < _minimumTabWidth)
+ width = _minimumTabWidth;
+
+ 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);
- closeButton = crossRect.Contains(position);
+ var crossRect = new Rectangle(x + width - DockPanel.DefaultButtonsSize - DockPanel.DefaultButtonsMargin, (HeaderRectangle.Height - DockPanel.DefaultButtonsSize) / 2, DockPanel.DefaultButtonsSize, DockPanel.DefaultButtonsSize);
+ closeButton = crossRect.Contains(position) && IsCloseButtonVisible(tab, closeButtonVisibility);
result = tab;
break;
}
@@ -112,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));
@@ -129,10 +162,10 @@ 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, DockPanel.DefaultHeaderHeight);
+ bounds = new Rectangle(x, 0, width, HeaderRectangle.Height);
return;
}
x += width;
@@ -212,7 +245,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);
}
@@ -221,17 +254,20 @@ 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);
- 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
{
@@ -245,10 +281,14 @@ 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;
- var tabRect = new Rectangle(x, 0, width, DockPanel.DefaultHeaderHeight);
+
+ float width = CalculateTabWidth(tab, closeButtonVisibility);
+
+ 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;
@@ -275,7 +315,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);
}
@@ -284,27 +324,27 @@ 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);
// Draw cross
- if (isSelected || isMouseOver)
+ if (IsCloseButtonVisible(tab, closeButtonVisibility))
{
- 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);
Render2D.DrawSprite(style.Cross, crossRect, isMouseOverCross ? style.Foreground : style.ForegroundGrey);
}
- // Move
+ // Set the start position for the next tab
x += width;
}
// 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);
}
}
@@ -522,7 +562,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 e86f5cf42..4c1061d4a 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.
///
@@ -167,15 +186,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 +193,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 +261,66 @@ 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 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.
+ ///
+ [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.
+ ///
+ [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 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.
///