From dbd85fddfec6208ba09ad129598dc8aedb44159e Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 12 Sep 2023 22:24:15 +0200 Subject: [PATCH] Refactor Editor `Tab` controls system to allows customizing the tab header controls --- Source/Editor/GUI/Tabs/Tab.cs | 9 +++ Source/Editor/GUI/Tabs/Tabs.cs | 112 +++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 40 deletions(-) diff --git a/Source/Editor/GUI/Tabs/Tab.cs b/Source/Editor/GUI/Tabs/Tab.cs index 67be0259b..3968cc17d 100644 --- a/Source/Editor/GUI/Tabs/Tab.cs +++ b/Source/Editor/GUI/Tabs/Tab.cs @@ -77,5 +77,14 @@ namespace FlaxEditor.GUI.Tabs { Deselected?.Invoke(this); } + + /// + /// Factory method for tabs header control (UI for the tabs strip to represent this tab). + /// + /// The tab header control. + public virtual Tabs.TabHeader CreateHeader() + { + return new Tabs.TabHeader((Tabs)Parent, this); + } } } diff --git a/Source/Editor/GUI/Tabs/Tabs.cs b/Source/Editor/GUI/Tabs/Tabs.cs index e01178c3d..f9bfd3f6c 100644 --- a/Source/Editor/GUI/Tabs/Tabs.cs +++ b/Source/Editor/GUI/Tabs/Tabs.cs @@ -24,11 +24,6 @@ namespace FlaxEditor.GUI.Tabs /// protected Tabs Tabs; - /// - /// The index of the tab. - /// - protected int Index; - /// /// The tab. /// @@ -38,21 +33,22 @@ namespace FlaxEditor.GUI.Tabs /// Initializes a new instance of the class. /// /// The tabs. - /// The tab index. /// The tab. - public TabHeader(Tabs tabs, int index, Tab tab) + public TabHeader(Tabs tabs, Tab tab) : base(Float2.Zero, tabs._tabsSize) { Tabs = tabs; - Index = index; Tab = tab; } /// public override bool OnMouseUp(Float2 location, MouseButton button) { - Tabs.SelectedTabIndex = Index; - Tabs.Focus(); + if (EnabledInHierarchy && Tab.Enabled) + { + Tabs.SelectedTab = Tab; + Tabs.Focus(); + } return true; } @@ -61,19 +57,20 @@ namespace FlaxEditor.GUI.Tabs { base.Draw(); - // Cache data var style = Style.Current; + var enabled = EnabledInHierarchy && Tab.EnabledInHierarchy; var tabRect = new Rectangle(Float2.Zero, Size); - bool isTabSelected = Tabs._selectedIndex == Index; - bool isMouseOverTab = IsMouseOver; var textOffset = Tabs._orientation == Orientation.Horizontal ? 0 : 8; // Draw bar - if (isTabSelected) + if (Tabs.SelectedTab == Tab) { + var color = style.BackgroundSelected; + if (!enabled) + color *= 0.6f; if (Tabs._orientation == Orientation.Horizontal) { - Render2D.FillRectangle(tabRect, style.BackgroundSelected); + Render2D.FillRectangle(tabRect, color); } else { @@ -84,10 +81,10 @@ namespace FlaxEditor.GUI.Tabs fillRect.Size.X -= lefEdgeWidth; fillRect.Location.X += lefEdgeWidth; 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); } @@ -131,20 +128,15 @@ namespace FlaxEditor.GUI.Tabs { 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 + var pos = Float2.Zero; + var sizeMask = Tabs._orientation == Orientation.Horizontal ? Float2.UnitX : Float2.UnitY; for (int i = 0; i < Children.Count; i++) { if (Children[i] is TabHeader tabHeader) { - tabHeader.Bounds = tabRect; - tabRect.Offset(tabStripOffset); + tabHeader.Location = pos; + pos += tabHeader.Size * sizeMask; } } } @@ -160,6 +152,11 @@ namespace FlaxEditor.GUI.Tabs /// protected Float2 _tabsSize; + /// + /// Automatic tab size based on the fill axis. + /// + protected bool _autoTabsSizeAuto; + /// /// The orientation. /// @@ -174,15 +171,31 @@ namespace FlaxEditor.GUI.Tabs set { _tabsSize = value; - for (int i = 0; i < TabsPanel.ChildrenCount; i++) + if (!_autoTabsSizeAuto) { - if (TabsPanel.Children[i] is TabHeader tabHeader) - tabHeader.Size = value; + for (int i = 0; i < TabsPanel.ChildrenCount; i++) + { + if (TabsPanel.Children[i] is TabHeader tabHeader) + tabHeader.Size = value; + } } PerformLayout(); } } + /// + /// Enables automatic tabs size to fill the space. + /// + public bool AutoTabsSize + { + get => _autoTabsSizeAuto; + set + { + _autoTabsSizeAuto = value; + PerformLayout(); + } + } + /// /// Gets or sets the orientation. /// @@ -339,14 +352,10 @@ namespace FlaxEditor.GUI.Tabs // Update tabs headers TabsPanel.DisposeChildren(); - int index = 0; for (int i = 0; i < Children.Count; i++) { if (Children[i] is Tab tab) - { - var tabHeader = new TabHeader(this, index++, tab); - TabsPanel.AddChild(tabHeader); - } + TabsPanel.AddChild(tab.CreateHeader()); } TabsPanel.IsLayoutLocked = wasLocked; @@ -371,23 +380,46 @@ namespace FlaxEditor.GUI.Tabs /// 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 - 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 - 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++) { if (Children[i] is Tab tab) { - // Check if is selected or not if (i - 1 == _selectedIndex) { // Show and fit size 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 {