Refactor Editor Tab controls system to allows customizing the tab header controls

This commit is contained in:
Wojtek Figat
2023-09-12 22:24:15 +02:00
parent 3e1940c799
commit dbd85fddfe
2 changed files with 81 additions and 40 deletions

View File

@@ -77,5 +77,14 @@ namespace FlaxEditor.GUI.Tabs
{ {
Deselected?.Invoke(this); Deselected?.Invoke(this);
} }
/// <summary>
/// Factory method for tabs header control (UI for the tabs strip to represent this tab).
/// </summary>
/// <returns>The tab header control.</returns>
public virtual Tabs.TabHeader CreateHeader()
{
return new Tabs.TabHeader((Tabs)Parent, this);
}
} }
} }

View File

@@ -24,11 +24,6 @@ namespace FlaxEditor.GUI.Tabs
/// </summary> /// </summary>
protected Tabs Tabs; protected Tabs Tabs;
/// <summary>
/// The index of the tab.
/// </summary>
protected int Index;
/// <summary> /// <summary>
/// The tab. /// The tab.
/// </summary> /// </summary>
@@ -38,21 +33,22 @@ namespace FlaxEditor.GUI.Tabs
/// Initializes a new instance of the <see cref="TabHeader"/> class. /// Initializes a new instance of the <see cref="TabHeader"/> class.
/// </summary> /// </summary>
/// <param name="tabs">The tabs.</param> /// <param name="tabs">The tabs.</param>
/// <param name="index">The tab index.</param>
/// <param name="tab">The tab.</param> /// <param name="tab">The tab.</param>
public TabHeader(Tabs tabs, int index, Tab tab) public TabHeader(Tabs tabs, Tab tab)
: base(Float2.Zero, tabs._tabsSize) : base(Float2.Zero, tabs._tabsSize)
{ {
Tabs = tabs; Tabs = tabs;
Index = index;
Tab = tab; Tab = tab;
} }
/// <inheritdoc /> /// <inheritdoc />
public override bool OnMouseUp(Float2 location, MouseButton button) public override bool OnMouseUp(Float2 location, MouseButton button)
{ {
Tabs.SelectedTabIndex = Index; if (EnabledInHierarchy && Tab.Enabled)
Tabs.Focus(); {
Tabs.SelectedTab = Tab;
Tabs.Focus();
}
return true; return true;
} }
@@ -61,19 +57,20 @@ namespace FlaxEditor.GUI.Tabs
{ {
base.Draw(); base.Draw();
// Cache data
var style = Style.Current; var style = Style.Current;
var enabled = EnabledInHierarchy && Tab.EnabledInHierarchy;
var tabRect = new Rectangle(Float2.Zero, Size); var tabRect = new Rectangle(Float2.Zero, Size);
bool isTabSelected = Tabs._selectedIndex == Index;
bool isMouseOverTab = IsMouseOver;
var textOffset = Tabs._orientation == Orientation.Horizontal ? 0 : 8; var textOffset = Tabs._orientation == Orientation.Horizontal ? 0 : 8;
// Draw bar // Draw bar
if (isTabSelected) if (Tabs.SelectedTab == Tab)
{ {
var color = style.BackgroundSelected;
if (!enabled)
color *= 0.6f;
if (Tabs._orientation == Orientation.Horizontal) if (Tabs._orientation == Orientation.Horizontal)
{ {
Render2D.FillRectangle(tabRect, style.BackgroundSelected); Render2D.FillRectangle(tabRect, color);
} }
else else
{ {
@@ -84,10 +81,10 @@ namespace FlaxEditor.GUI.Tabs
fillRect.Size.X -= lefEdgeWidth; fillRect.Size.X -= lefEdgeWidth;
fillRect.Location.X += lefEdgeWidth; fillRect.Location.X += lefEdgeWidth;
Render2D.FillRectangle(fillRect, style.Background); Render2D.FillRectangle(fillRect, style.Background);
Render2D.FillRectangle(leftEdgeRect, style.BackgroundSelected); Render2D.FillRectangle(leftEdgeRect, color);
} }
} }
else if (isMouseOverTab) else if (IsMouseOver && enabled)
{ {
Render2D.FillRectangle(tabRect, style.BackgroundHighlighted); Render2D.FillRectangle(tabRect, style.BackgroundHighlighted);
} }
@@ -131,20 +128,15 @@ namespace FlaxEditor.GUI.Tabs
{ {
base.PerformLayoutBeforeChildren(); base.PerformLayoutBeforeChildren();
// Cache data
var tabsSize = Tabs._tabsSize;
var clientSize = GetClientArea();
tabsSize = Float2.Min(tabsSize, clientSize.Size);
var tabRect = new Rectangle(Float2.Zero, tabsSize);
var tabStripOffset = Tabs._orientation == Orientation.Horizontal ? new Float2(tabsSize.X, 0) : new Float2(0, tabsSize.Y);
// Arrange tab header controls // Arrange tab header controls
var pos = Float2.Zero;
var sizeMask = Tabs._orientation == Orientation.Horizontal ? Float2.UnitX : Float2.UnitY;
for (int i = 0; i < Children.Count; i++) for (int i = 0; i < Children.Count; i++)
{ {
if (Children[i] is TabHeader tabHeader) if (Children[i] is TabHeader tabHeader)
{ {
tabHeader.Bounds = tabRect; tabHeader.Location = pos;
tabRect.Offset(tabStripOffset); pos += tabHeader.Size * sizeMask;
} }
} }
} }
@@ -160,6 +152,11 @@ namespace FlaxEditor.GUI.Tabs
/// </summary> /// </summary>
protected Float2 _tabsSize; protected Float2 _tabsSize;
/// <summary>
/// Automatic tab size based on the fill axis.
/// </summary>
protected bool _autoTabsSizeAuto;
/// <summary> /// <summary>
/// The orientation. /// The orientation.
/// </summary> /// </summary>
@@ -174,15 +171,31 @@ namespace FlaxEditor.GUI.Tabs
set set
{ {
_tabsSize = value; _tabsSize = value;
for (int i = 0; i < TabsPanel.ChildrenCount; i++) if (!_autoTabsSizeAuto)
{ {
if (TabsPanel.Children[i] is TabHeader tabHeader) for (int i = 0; i < TabsPanel.ChildrenCount; i++)
tabHeader.Size = value; {
if (TabsPanel.Children[i] is TabHeader tabHeader)
tabHeader.Size = value;
}
} }
PerformLayout(); PerformLayout();
} }
} }
/// <summary>
/// Enables automatic tabs size to fill the space.
/// </summary>
public bool AutoTabsSize
{
get => _autoTabsSizeAuto;
set
{
_autoTabsSizeAuto = value;
PerformLayout();
}
}
/// <summary> /// <summary>
/// Gets or sets the orientation. /// Gets or sets the orientation.
/// </summary> /// </summary>
@@ -339,14 +352,10 @@ namespace FlaxEditor.GUI.Tabs
// Update tabs headers // Update tabs headers
TabsPanel.DisposeChildren(); TabsPanel.DisposeChildren();
int index = 0;
for (int i = 0; i < Children.Count; i++) for (int i = 0; i < Children.Count; i++)
{ {
if (Children[i] is Tab tab) if (Children[i] is Tab tab)
{ TabsPanel.AddChild(tab.CreateHeader());
var tabHeader = new TabHeader(this, index++, tab);
TabsPanel.AddChild(tabHeader);
}
} }
TabsPanel.IsLayoutLocked = wasLocked; TabsPanel.IsLayoutLocked = wasLocked;
@@ -371,23 +380,46 @@ namespace FlaxEditor.GUI.Tabs
/// <inheritdoc /> /// <inheritdoc />
protected override void PerformLayoutBeforeChildren() protected override void PerformLayoutBeforeChildren()
{ {
var tabsSize = _tabsSize;
if (_autoTabsSizeAuto)
{
// Horizontal is default for Toolbox so tabs go to the right
int tabsCount = 0;
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
{
if (TabsPanel.Children[i] is TabHeader)
tabsCount++;
}
if (tabsCount == 0)
tabsCount = 1;
if (_orientation == Orientation.Horizontal)
tabsSize.X = Width / tabsCount;
else
tabsSize.Y = Height / tabsCount;
for (int i = 0; i < TabsPanel.ChildrenCount; i++)
{
if (TabsPanel.Children[i] is TabHeader tabHeader)
tabHeader.Size = tabsSize;
}
}
// Fit the tabs panel // Fit the tabs panel
TabsPanel.Size = _orientation == Orientation.Horizontal ? new Float2(Width, _tabsSize.Y) : new Float2(_tabsSize.X, Height); TabsPanel.Size = _orientation == Orientation.Horizontal
? new Float2(Width, tabsSize.Y)
: new Float2(tabsSize.X, Height);
// Hide all pages except selected one // Hide all pages except selected one
var clientArea = _orientation == Orientation.Horizontal
? new Rectangle(0, _tabsSize.Y, Width, Height - _tabsSize.Y)
: new Rectangle(_tabsSize.X, 0, Width - _tabsSize.X, Height);
for (int i = 0; i < Children.Count; i++) for (int i = 0; i < Children.Count; i++)
{ {
if (Children[i] is Tab tab) if (Children[i] is Tab tab)
{ {
// Check if is selected or not
if (i - 1 == _selectedIndex) if (i - 1 == _selectedIndex)
{ {
// Show and fit size // Show and fit size
tab.Visible = true; tab.Visible = true;
tab.Bounds = clientArea; tab.Bounds = _orientation == Orientation.Horizontal
? new Rectangle(0, tabsSize.Y, Width, Height - tabsSize.Y)
: new Rectangle(tabsSize.X, 0, Width - tabsSize.X, Height);
} }
else else
{ {