]> git.lizzy.rs Git - irrlicht.git/commitdiff
CGUITabControl: Center selected tab whenever possible
authorSmallJoker <mk939@ymail.com>
Sat, 9 Jul 2022 20:15:34 +0000 (22:15 +0200)
committersfan5 <sfan5@live.de>
Fri, 23 Dec 2022 19:07:15 +0000 (20:07 +0100)
This greatly improves the navigation speed by clicking through the tabs
without losing track of the current scroll position.

source/Irrlicht/CGUITabControl.cpp
source/Irrlicht/CGUITabControl.h

index 04f36f9a9f1265cea1b6d7095a360f547869c9c8..30a31c39bb67f4792b6647fda411748bb154d661 100644 (file)
@@ -454,7 +454,7 @@ void CGUITabControl::scrollRight()
        recalculateScrollBar();\r
 }\r
 \r
-s32 CGUITabControl::calcTabWidth(s32 pos, IGUIFont* font, const wchar_t* text, bool withScrollControl) const\r
+s32 CGUITabControl::calcTabWidth(IGUIFont* font, const wchar_t* text) const\r
 {\r
        if ( !font )\r
                return 0;\r
@@ -463,26 +463,11 @@ s32 CGUITabControl::calcTabWidth(s32 pos, IGUIFont* font, const wchar_t* text, b
        if ( TabMaxWidth > 0 && len > TabMaxWidth )\r
                len = TabMaxWidth;\r
 \r
-       // check if we miss the place to draw the tab-button\r
-       if ( withScrollControl && ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )\r
-       {\r
-               s32 tabMinWidth = font->getDimension(L"A").Width;\r
-               if ( TabExtraWidth > 0 && TabExtraWidth > tabMinWidth )\r
-                       tabMinWidth = TabExtraWidth;\r
-\r
-               if ( ScrollControl && pos+tabMinWidth <= UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )\r
-               {\r
-                       len = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;\r
-               }\r
-       }\r
        return len;\r
 }\r
 \r
-bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl)\r
+bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl, s32 *pos_rightmost)\r
 {\r
-       if ( startIndex >= (s32)Tabs.size() )\r
-               startIndex -= 1;\r
-\r
        if ( startIndex < 0 )\r
                startIndex = 0;\r
 \r
@@ -492,17 +477,18 @@ bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl)
 \r
        IGUIFont* font = skin->getFont();\r
 \r
-       core::rect<s32> frameRect(AbsoluteRect);\r
-\r
        if (Tabs.empty())\r
                return false;\r
 \r
        if (!font)\r
                return false;\r
 \r
-       s32 pos = frameRect.UpperLeftCorner.X + 2;\r
+       s32 pos = AbsoluteRect.UpperLeftCorner.X + 2;\r
+       const s32 pos_right = withScrollControl ?\r
+               UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 :\r
+               AbsoluteRect.LowerRightCorner.X;\r
 \r
-       for (s32 i=startIndex; i<(s32)Tabs.size(); ++i)\r
+       for (s32 i = startIndex; i < (s32)Tabs.size(); ++i)\r
        {\r
                // get Text\r
                const wchar_t* text = 0;\r
@@ -511,26 +497,71 @@ bool CGUITabControl::needScrollControl(s32 startIndex, bool withScrollControl)
                        text = Tabs[i]->getText();\r
 \r
                        // get text length\r
-                       s32 len = calcTabWidth(pos, font, text, false); // always without withScrollControl here or len would be shortened\r
-\r
-                       frameRect.LowerRightCorner.X += len;\r
-\r
-                       frameRect.UpperLeftCorner.X = pos;\r
-                       frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;\r
+                       s32 len = calcTabWidth(font, text);     // always without withScrollControl here or len would be shortened\r
                        pos += len;\r
                }\r
 \r
-               if ( withScrollControl && pos > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2)\r
-                       return true;\r
-\r
-               if ( !withScrollControl && pos > AbsoluteRect.LowerRightCorner.X )\r
+               if (pos > pos_right)\r
                        return true;\r
        }\r
 \r
+       if (pos_rightmost)\r
+               *pos_rightmost = pos;\r
        return false;\r
 }\r
 \r
 \r
+s32 CGUITabControl::calculateScrollIndexFromActive()\r
+{\r
+       if (!ScrollControl || Tabs.empty())\r
+               return 0;\r
+\r
+       IGUISkin *skin = Environment->getSkin();\r
+       if (!skin)\r
+               return false;\r
+\r
+       IGUIFont *font = skin->getFont();\r
+       if (!font)\r
+               return false;\r
+\r
+       const s32 pos_left = AbsoluteRect.UpperLeftCorner.X + 2;\r
+       const s32 pos_right = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2;\r
+\r
+       // Move from center to the left border left until it is reached\r
+       s32 pos_cl = (pos_left + pos_right) / 2;\r
+       s32 i = ActiveTabIndex;\r
+       for (; i > 0; --i) {\r
+               if (!Tabs[i])\r
+                       continue;\r
+\r
+               s32 len = calcTabWidth(font, Tabs[i]->getText());\r
+               if (i == ActiveTabIndex)\r
+                       len /= 2;\r
+               if (pos_cl - len < pos_left)\r
+                       break;\r
+\r
+               pos_cl -= len;\r
+       }\r
+       if (i == 0)\r
+               return i;\r
+\r
+       // Is scrolling to right still possible?\r
+       s32 pos_rr = 0;\r
+       if (needScrollControl(i, true, &pos_rr))\r
+               return i; // Yes? -> OK\r
+\r
+       // No? -> Decrease "i" more. Append tabs until scrolling becomes necessary\r
+       for (--i; i > 0; --i) {\r
+               if (!Tabs[i])\r
+                       continue;\r
+\r
+               pos_rr += calcTabWidth(font, Tabs[i]->getText());\r
+               if (pos_rr > pos_right)\r
+                       break;\r
+       }\r
+       return i + 1;\r
+}\r
+\r
 core::rect<s32> CGUITabControl::calcTabPos()\r
 {\r
        core::rect<s32> r;\r
@@ -613,7 +644,7 @@ void CGUITabControl::draw()
        IGUITab *activeTab = 0;\r
 \r
        // Draw all tab-buttons except the active one\r
-       for (u32 i=CurrentScrollTabIndex; i<Tabs.size(); ++i)\r
+       for (u32 i = CurrentScrollTabIndex; i < Tabs.size() && !needRightScroll; ++i)\r
        {\r
                // get Text\r
                const wchar_t* text = 0;\r
@@ -621,11 +652,13 @@ void CGUITabControl::draw()
                        text = Tabs[i]->getText();\r
 \r
                // get text length\r
-               s32 len = calcTabWidth(pos, font, text, true);\r
-               if ( ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )\r
-               {\r
-                       needRightScroll = true;\r
-                       break;\r
+               s32 len = calcTabWidth(font, text);\r
+               if (ScrollControl) {\r
+                       s32 space = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;\r
+                       if (space < len) {\r
+                               needRightScroll = true;\r
+                               len = space;\r
+                       }\r
                }\r
 \r
                frameRect.LowerRightCorner.X += len;\r
@@ -794,6 +827,7 @@ s32 CGUITabControl::getTabExtraWidth() const
 \r
 void CGUITabControl::recalculateScrollBar()\r
 {\r
+       // Down: to right, Up: to left\r
        if (!UpButton || !DownButton)\r
                return;\r
 \r
@@ -894,7 +928,8 @@ s32 CGUITabControl::getTabAt(s32 xpos, s32 ypos) const
        if (!frameRect.isPointInside(p))\r
                return -1;\r
 \r
-       for (s32 i=CurrentScrollTabIndex; i<(s32)Tabs.size(); ++i)\r
+       bool abort = false;\r
+       for (s32 i = CurrentScrollTabIndex; i < (s32)Tabs.size() && !abort; ++i)\r
        {\r
                // get Text\r
                const wchar_t* text = 0;\r
@@ -902,9 +937,15 @@ s32 CGUITabControl::getTabAt(s32 xpos, s32 ypos) const
                        text = Tabs[i]->getText();\r
 \r
                // get text length\r
-               s32 len = calcTabWidth(pos, font, text, true);\r
-               if ( ScrollControl && pos+len > UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 )\r
-                       return -1;\r
+               s32 len = calcTabWidth(font, text);\r
+               if (ScrollControl) {\r
+                       // TODO: merge this with draw() ?\r
+                       s32 space = UpButton->getAbsolutePosition().UpperLeftCorner.X - 2 - pos;\r
+                       if (space < len) {\r
+                               abort = true;\r
+                               len = space;\r
+                       }\r
+               }\r
 \r
                frameRect.UpperLeftCorner.X = pos;\r
                frameRect.LowerRightCorner.X = frameRect.UpperLeftCorner.X + len;\r
@@ -915,6 +956,7 @@ s32 CGUITabControl::getTabAt(s32 xpos, s32 ypos) const
                {\r
                        return i;\r
                }\r
+\r
        }\r
        return -1;\r
 }\r
@@ -948,6 +990,11 @@ bool CGUITabControl::setActiveTab(s32 idx)
                Parent->OnEvent(event);\r
        }\r
 \r
+       if (ScrollControl) {\r
+               CurrentScrollTabIndex = calculateScrollIndexFromActive();\r
+               recalculateScrollBar();\r
+       }\r
+\r
        return true;\r
 }\r
 \r
index ffc31f216a064cd71513e728e596bc8a61ab7d68..d5e8315f1687c8668d975f3b5834824d4ee45ff5 100644 (file)
@@ -153,8 +153,11 @@ namespace gui
 \r
                void scrollLeft();\r
                void scrollRight();\r
-               bool needScrollControl( s32 startIndex=0, bool withScrollControl=false );\r
-               s32 calcTabWidth(s32 pos, IGUIFont* font, const wchar_t* text, bool withScrollControl ) const;\r
+               //! Indicates whether the tabs overflow in X direction\r
+               bool needScrollControl( s32 startIndex=0, bool withScrollControl=false, s32 *pos_rightmost=nullptr );\r
+               //! Left index calculation based on the selected tab\r
+               s32 calculateScrollIndexFromActive();\r
+               s32 calcTabWidth(IGUIFont* font, const wchar_t* text) const;\r
                core::rect<s32> calcTabPos();\r
                void setVisibleTab(s32 idx);\r
                void removeTabButNotChild(s32 idx);\r