From a4190c35493a50641014af1b2ec2c13638e49f41 Mon Sep 17 00:00:00 2001
From: Yohann Ferreira <yohann_dot_ferreira_at_orange_dot_efer>
Date: Tue, 8 Mar 2011 23:52:24 +0100
Subject: Added a tab scrolling system in tabarea.

Currently working on the chat window.

Resolves: Mana-Mantis #308.

Reviewed-by: Thorbjorn.
---
 src/gui/widgets/tabbedarea.cpp | 147 ++++++++++++++++++++++++++++++++++++++++-
 src/gui/widgets/tabbedarea.h   |  43 ++++++++++++
 2 files changed, 189 insertions(+), 1 deletion(-)

(limited to 'src')

diff --git a/src/gui/widgets/tabbedarea.cpp b/src/gui/widgets/tabbedarea.cpp
index d93b4bb02..e0bf9e12f 100644
--- a/src/gui/widgets/tabbedarea.cpp
+++ b/src/gui/widgets/tabbedarea.cpp
@@ -28,11 +28,20 @@
 
 #include <guichan/widgets/container.hpp>
 
-TabbedArea::TabbedArea() : gcn::TabbedArea()
+TabbedArea::TabbedArea() : gcn::TabbedArea(),
+                           mTabsWidth(0),
+                           mVisibleTabsWidth(0),
+                           mTabScrollIndex(0)
 {
     mWidgetContainer->setOpaque(false);
     addWidgetListener(this);
 
+    mArrowButton[0] = new Button("<", "shift_left", this);
+    mArrowButton[1] = new Button(">", "shift_right", this);
+
+    add(mArrowButton[0]);
+    add(mArrowButton[1]);
+
     widgetResized(NULL);
 }
 
@@ -96,6 +105,9 @@ void TabbedArea::addTab(gcn::Tab* tab, gcn::Widget* widget)
     int width = getWidth() - 2 * getFrameSize();
     int height = getHeight() - 2 * getFrameSize() - mTabContainer->getHeight();
     widget->setSize(width, height);
+
+    updateTabsWidth();
+    updateArrowEnableState();
 }
 
 void TabbedArea::addTab(const std::string &caption, gcn::Widget *widget)
@@ -159,6 +171,7 @@ void TabbedArea::removeTab(Tab *tab)
     }
 
     adjustSize();
+    updateTabsWidth();
     adjustTabPositions();
 }
 
@@ -209,6 +222,138 @@ void TabbedArea::widgetResized(const gcn::Event &event _UNUSED_)
     gcn::Widget *w = getCurrentWidget();
     if (w)
         w->setSize(width, height);
+
+    // Check whether there is room to show more tabs now.
+    int innerWidth = getWidth() - 4 - mArrowButton[0]->getWidth()
+        - mArrowButton[1]->getWidth();
+    int newWidth = mVisibleTabsWidth;
+    while (mTabScrollIndex && newWidth < innerWidth)
+    {
+        newWidth += mTabs[mTabScrollIndex - 1].first->getWidth();
+        if (newWidth < innerWidth)
+            --mTabScrollIndex;
+    }
+
+    // Move the right arrow to fit the windows content.
+    mArrowButton[1]->setPosition(width - mArrowButton[1]->getWidth(), 0);
+
+    updateArrowEnableState();
+    adjustTabPositions();
+}
+
+void TabbedArea::updateTabsWidth()
+{
+    mTabsWidth = 0;
+    for (TabContainer::const_iterator itr = mTabs.begin(), itr_end = mTabs.end();
+         itr != itr_end; ++itr)
+    {
+        mTabsWidth += (*itr).first->getWidth();
+    }
+    updateVisibleTabsWidth();
+}
+
+void TabbedArea::updateVisibleTabsWidth()
+{
+    mVisibleTabsWidth = 0;
+    for (unsigned int i = mTabScrollIndex; i < mTabs.size(); ++i)
+    {
+        mVisibleTabsWidth += mTabs[i].first->getWidth();
+    }
+}
+
+void TabbedArea::adjustTabPositions()
+{
+    int maxTabHeight = 0;
+    for (unsigned i = 0; i < mTabs.size(); ++i)
+    {
+        if (mTabs[i].first->getHeight() > maxTabHeight)
+        {
+            maxTabHeight = mTabs[i].first->getHeight();
+        }
+    }
+
+    int x = mArrowButton[0]->isVisible() ? mArrowButton[0]->getWidth() : 0;
+    for (unsigned i = mTabScrollIndex; i < mTabs.size(); ++i)
+    {
+        gcn::Tab* tab = mTabs[i].first;
+        tab->setPosition(x, maxTabHeight - tab->getHeight());
+        x += tab->getWidth();
+    }
+
+    // If the tabs are scrolled, we hide them away.
+    if (mTabScrollIndex > 0)
+    {
+        x = 0;
+        for (unsigned i = 0; i < mTabScrollIndex; ++i)
+        {
+            gcn::Tab* tab = mTabs[i].first;
+            x -= tab->getWidth();
+            tab->setPosition(x, maxTabHeight - tab->getHeight());
+        }
+    }
+}
+
+void TabbedArea::action(const gcn::ActionEvent& actionEvent)
+{
+    Widget* source = actionEvent.getSource();
+    Tab* tab = dynamic_cast<Tab*>(source);
+
+    if (tab)
+    {
+        setSelectedTab(tab);
+    }
+    else
+    {
+        if (actionEvent.getId() == "shift_left")
+        {
+            if (mTabScrollIndex)
+                --mTabScrollIndex;
+        }
+        else if (actionEvent.getId() == "shift_right")
+        {
+            if (mTabScrollIndex < mTabs.size() - 1)
+                ++mTabScrollIndex;
+        }
+        adjustTabPositions();
+
+        updateArrowEnableState();
+    }
+}
+
+void TabbedArea::updateArrowEnableState()
+{
+    updateTabsWidth();
+    if (mTabsWidth > getWidth() - 4
+        - mArrowButton[0]->getWidth()
+        - mArrowButton[1]->getWidth())
+    {
+        mArrowButton[0]->setVisible(true);
+        mArrowButton[1]->setVisible(true);
+    }
+    else
+    {
+        mArrowButton[0]->setVisible(false);
+        mArrowButton[1]->setVisible(false);
+        mTabScrollIndex = 0;
+    }
+
+    // Left arrow consistency check
+    if (!mTabScrollIndex)
+        mArrowButton[0]->setEnabled(false);
+    else
+        mArrowButton[0]->setEnabled(true);
+
+    // Right arrow consistency check
+    if (mVisibleTabsWidth < getWidth() - 4
+        - mArrowButton[0]->getWidth()
+        - mArrowButton[1]->getWidth())
+    {
+        mArrowButton[1]->setEnabled(false);
+    }
+    else
+    {
+        mArrowButton[1]->setEnabled(true);
+    }
 }
 
 /*
diff --git a/src/gui/widgets/tabbedarea.h b/src/gui/widgets/tabbedarea.h
index aff47929d..e5a00e277 100644
--- a/src/gui/widgets/tabbedarea.h
+++ b/src/gui/widgets/tabbedarea.h
@@ -28,6 +28,8 @@
 #include <guichan/widgets/container.hpp>
 #include <guichan/widgets/tabbedarea.hpp>
 
+#include "gui/widgets/button.h"
+
 #include <string>
 
 #ifdef __GNUC__
@@ -119,12 +121,53 @@ class TabbedArea : public gcn::TabbedArea, public gcn::WidgetListener
 
         void moveRight(gcn::Tab *tab);
 */
+        void adjustTabPositions();
+
+        void action(const gcn::ActionEvent& actionEvent);
+
         // Inherited from MouseListener
 
         void mousePressed(gcn::MouseEvent &mouseEvent);
 
     private:
         typedef std::vector< std::pair<gcn::Tab*, gcn::Widget*> > TabContainer;
+
+        /** The tab arrows */
+        gcn::Button *mArrowButton[2];
+
+        /** Check whether the arrow should be clickable */
+        void updateArrowEnableState();
+
+        /**
+         * Update the overall width of all tab. Used to know whether the arrows
+         * have to be drawn or not.
+         */
+        void updateTabsWidth();
+
+        /**
+         * The overall width of all tab.
+         */
+        int mTabsWidth;
+
+        /**
+         * Update the overall width of visible tab. Used to know whether
+         * the arrows have to be enable or not.
+         */
+        void updateVisibleTabsWidth();
+
+        /**
+         * The overall width of visible tab.
+         */
+        int mVisibleTabsWidth;
+
+
+        /**
+         * The tab scroll index. When scrolling with the arrows, the tabs
+         * must be displayed according to the current index.
+         * So the first tab displayed may not be the first in the list.
+         * @note the index must start at 0.
+         */
+        unsigned mTabScrollIndex;
 };
 
 #endif
-- 
cgit v1.2.3-70-g09d2