ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

tab.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/util.h"
00024 #include "gui/widgets/tab.h"
00025 #include "gui/gui-manager.h"
00026 
00027 #include "gui/ThemeEval.h"
00028 
00029 namespace GUI {
00030 
00031 enum {
00032     kCmdLeft  = 'LEFT',
00033     kCmdRight = 'RGHT'
00034 };
00035 
00036 static const int kTabTitleSpacing = 2 * 5;
00037 
00038 TabWidget::TabWidget(GuiObject *boss, int x, int y, int w, int h)
00039     : Widget(boss, x, y, w, h), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
00040     init();
00041 }
00042 
00043 TabWidget::TabWidget(GuiObject *boss, const String &name)
00044     : Widget(boss, name), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
00045     init();
00046 }
00047 
00048 void TabWidget::init() {
00049     setFlags(WIDGET_ENABLED);
00050     _type = kTabWidget;
00051     _activeTab = -1;
00052     _firstVisibleTab = 0;
00053     _lastVisibleTab = 0;
00054     _navButtonsVisible = false;
00055 
00056     _minTabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
00057     _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
00058     _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
00059 
00060     _bodyTP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Top");
00061     _bodyBP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Bottom");
00062     _bodyLP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Left");
00063     _bodyRP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Right");
00064 
00065     _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButtonPadding.Right", 0);
00066     _butTP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Padding.Top", 0);
00067     _butW = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Width", 10);
00068     _butH = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Height", 10);
00069 
00070     int x = _w - _butRP - _butW * 2 - 2;
00071     int y = _butTP - _tabHeight;
00072     _navLeft = new ButtonWidget(this, x, y, _butW, _butH, "<", 0, kCmdLeft);
00073     _navRight = new ButtonWidget(this, x + _butW + 2, y, _butW, _butH, ">", 0, kCmdRight);
00074 }
00075 
00076 TabWidget::~TabWidget() {
00077     // If widgets were added or removed in the current tab, without tabs
00078     // having been switched using setActiveTab() afterward, then the
00079     // firstWidget in the _tabs list for the active tab may not be up to
00080     // date. So update it now.
00081     if (_activeTab != -1)
00082         _tabs[_activeTab].firstWidget = _firstWidget;
00083     _firstWidget = 0;
00084     for (uint i = 0; i < _tabs.size(); ++i) {
00085         delete _tabs[i].firstWidget;
00086         _tabs[i].firstWidget = 0;
00087     }
00088     _tabs.clear();
00089     delete _navRight;
00090 }
00091 
00092 int16 TabWidget::getChildY() const {
00093     // NOTE: if you change that, make sure to do the same
00094     // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
00095     return getAbsY() + _tabHeight;
00096 }
00097 
00098 uint16 TabWidget::getHeight() const {
00099     // NOTE: if you change that, make sure to do the same
00100     // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
00101     // NOTE: this height is used for clipping, so it *includes*
00102     // tabs, because it starts from getAbsY(), not getChildY()
00103     return _h + _tabHeight;
00104 }
00105 
00106 int TabWidget::addTab(const String &title) {
00107     // Add a new tab page
00108     Tab newTab;
00109     newTab.title = title;
00110     newTab.firstWidget = 0;
00111 
00112     // Determine the new tab width
00113     int newWidth = g_gui.getStringWidth(title) + kTabTitleSpacing;
00114     if (newWidth < _minTabWidth)
00115         newWidth = _minTabWidth;
00116     newTab._tabWidth = newWidth;
00117 
00118     _tabs.push_back(newTab);
00119 
00120     int numTabs = _tabs.size();
00121 
00122     // Activate the new tab
00123     setActiveTab(numTabs - 1);
00124 
00125     return _activeTab;
00126 }
00127 
00128 void TabWidget::removeTab(int tabID) {
00129     assert(0 <= tabID && tabID < (int)_tabs.size());
00130 
00131     // Deactivate the tab if it's currently the active one
00132     if (tabID == _activeTab) {
00133         _tabs[tabID].firstWidget = _firstWidget;
00134         releaseFocus();
00135         _firstWidget = 0;
00136     }
00137 
00138     // Dispose the widgets in that tab and then the tab itself
00139     delete _tabs[tabID].firstWidget;
00140     _tabs.remove_at(tabID);
00141 
00142     // Adjust _firstVisibleTab if necessary
00143     if (_firstVisibleTab >= (int)_tabs.size()) {
00144         _firstVisibleTab = MAX(0, (int)_tabs.size() - 1);
00145     }
00146 
00147     // The active tab was removed, so select a new active one (if any remains)
00148     if (tabID == _activeTab) {
00149         _activeTab = -1;
00150         if (tabID >= (int)_tabs.size())
00151             tabID = _tabs.size() - 1;
00152         if (tabID >= 0)
00153             setActiveTab(tabID);
00154     }
00155 
00156     // Finally trigger a redraw
00157     g_gui.scheduleTopDialogRedraw();
00158 }
00159 
00160 void TabWidget::setActiveTab(int tabID) {
00161     assert(0 <= tabID && tabID < (int)_tabs.size());
00162     if (_activeTab != tabID) {
00163         // Exchange the widget lists, and switch to the new tab
00164         if (_activeTab != -1) {
00165             _tabs[_activeTab].firstWidget = _firstWidget;
00166             releaseFocus();
00167         }
00168         _activeTab = tabID;
00169         _firstWidget = _tabs[tabID].firstWidget;
00170 
00171         // Also ensure the tab is visible in the tab bar
00172         if (_firstVisibleTab > tabID)
00173             setFirstVisible(tabID, true);
00174         while (_lastVisibleTab < tabID)
00175             setFirstVisible(_firstVisibleTab + 1, false);
00176 
00177         g_gui.scheduleTopDialogRedraw();
00178     }
00179 }
00180 
00181 
00182 void TabWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
00183     Widget::handleCommand(sender, cmd, data);
00184 
00185     switch (cmd) {
00186     case kCmdLeft:
00187         if (_firstVisibleTab > 0) {
00188             setFirstVisible(_firstVisibleTab - 1);
00189         }
00190         break;
00191 
00192     case kCmdRight:
00193         if (_lastVisibleTab + 1 < (int)_tabs.size()) {
00194             setFirstVisible(_firstVisibleTab + 1, false);
00195         }
00196         break;
00197     }
00198 }
00199 
00200 void TabWidget::handleMouseDown(int x, int y, int button, int clickCount) {
00201     assert(y < _tabHeight);
00202 
00203     if (x < 0)
00204         return;
00205 
00206     // Determine which tab was clicked
00207     int tabID;
00208     for (tabID = _firstVisibleTab; tabID <= _lastVisibleTab; ++tabID) {
00209         x -= _tabs[tabID]._tabWidth;
00210         if (x < 0)
00211             break;
00212     }
00213 
00214     // If a tab was clicked, switch to that pane
00215     if (tabID <= _lastVisibleTab)
00216         setActiveTab(tabID);
00217 }
00218 
00219 bool TabWidget::handleKeyDown(Common::KeyState state) {
00220     if (state.hasFlags(Common::KBD_SHIFT) && state.keycode == Common::KEYCODE_TAB)
00221         adjustTabs(kTabBackwards);
00222     else if (state.keycode == Common::KEYCODE_TAB)
00223         adjustTabs(kTabForwards);
00224 
00225     return Widget::handleKeyDown(state);
00226 }
00227 
00228 void TabWidget::adjustTabs(int value) {
00229     // Determine which tab is next
00230     int tabID = _activeTab + value;
00231     if (tabID >= (int)_tabs.size())
00232         tabID = 0;
00233     else if (tabID < 0)
00234         tabID = ((int)_tabs.size() - 1);
00235 
00236     setActiveTab(tabID);
00237 }
00238 
00239 int TabWidget::getFirstVisible() const {
00240     return _firstVisibleTab;
00241 }
00242 
00243 void TabWidget::setFirstVisible(int tabID, bool adjustIfRoom) {
00244     assert(0 <= tabID && tabID < (int)_tabs.size());
00245     _firstVisibleTab = tabID;
00246 
00247     computeLastVisibleTab(adjustIfRoom);
00248 
00249     g_gui.scheduleTopDialogRedraw(); // TODO: Necessary?
00250 }
00251 
00252 void TabWidget::reflowLayout() {
00253     Widget::reflowLayout();
00254 
00255     // NOTE: if you change that, make sure to do the same
00256     // changes in the ThemeLayoutTabWidget (gui/ThemeLayout.cpp)
00257     _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
00258     _minTabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
00259     _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
00260 
00261     _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.PaddingRight", 0);
00262     _butTP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Padding.Top", 0);
00263     _butW = g_gui.xmlEval()->getVar("GlobalsTabWidget.NavButton.Width", 10);
00264     _butH = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Height", 10);
00265 
00266     // If widgets were added or removed in the current tab, without tabs
00267     // having been switched using setActiveTab() afterward, then the
00268     // firstWidget in the _tabs list for the active tab may not be up to
00269     // date. So update it now.
00270     if (_activeTab != -1)
00271         _tabs[_activeTab].firstWidget = _firstWidget;
00272 
00273     for (uint i = 0; i < _tabs.size(); ++i) {
00274         Widget *w = _tabs[i].firstWidget;
00275         while (w) {
00276             w->reflowLayout();
00277             w = w->next();
00278         }
00279     }
00280 
00281     for (uint i = 0; i < _tabs.size(); ++i) {
00282         // Determine the new tab width
00283         int newWidth = g_gui.getStringWidth(_tabs[i].title) + kTabTitleSpacing;
00284         if (newWidth < _minTabWidth)
00285             newWidth = _minTabWidth;
00286         _tabs[i]._tabWidth = newWidth;
00287     }
00288 
00289     // See how many tabs fit on screen.
00290     // We do this in a loop, because it will change if we need to
00291     // add left/right scroll buttons, if we scroll left to use free
00292     // space on the right, or a combination of those.
00293     _navButtonsVisible = _firstVisibleTab > 0;
00294     do {
00295         computeLastVisibleTab(true);
00296 
00297         if (_firstVisibleTab > 0 || _lastVisibleTab + 1 < (int)_tabs.size()) {
00298             if (!_navButtonsVisible)
00299                 _navButtonsVisible = true;
00300             else
00301                 break;
00302         } else {
00303             if (_navButtonsVisible)
00304                 _navButtonsVisible = false;
00305             else
00306                 break;
00307         }
00308     } while (true);
00309 
00310     int x = _w - _butRP - _butW * 2 - 2;
00311     int y = _butTP - _tabHeight;
00312     _navLeft->resize(x, y, _butW, _butH);
00313     _navRight->resize(x + _butW + 2, y, _butW, _butH);
00314 }
00315 
00316 void TabWidget::drawWidget() {
00317     Common::Array<Common::String> tabs;
00318     Common::Array<int> widths;
00319     for (int i = _firstVisibleTab; i <= _lastVisibleTab; ++i) {
00320         tabs.push_back(_tabs[i].title);
00321         widths.push_back(_tabs[i]._tabWidth);
00322     }
00323     g_gui.theme()->drawDialogBackground(
00324             Common::Rect(_x + _bodyLP, _y + _bodyTP, _x + _w - _bodyRP, _y + _h - _bodyBP + _tabHeight),
00325             _bodyBackgroundType);
00326 
00327     g_gui.theme()->drawTab(Common::Rect(_x, _y, _x + _w, _y + _h), _tabHeight, widths, tabs,
00328                            _activeTab - _firstVisibleTab);
00329 }
00330 
00331 void TabWidget::draw() {
00332     Widget::draw();
00333 
00334     if (_navButtonsVisible) {
00335         _navLeft->draw();
00336         _navRight->draw();
00337     }
00338 }
00339 
00340 void TabWidget::markAsDirty() {
00341     Widget::markAsDirty();
00342 
00343     if (_navButtonsVisible) {
00344         _navLeft->markAsDirty();
00345         _navRight->markAsDirty();
00346     }
00347 }
00348 
00349 bool TabWidget::containsWidget(Widget *w) const {
00350     if (w == _navLeft || w == _navRight || _navLeft->containsWidget(w) || _navRight->containsWidget(w))
00351         return true;
00352     return containsWidgetInChain(_firstWidget, w);
00353 }
00354 
00355 
00356 Widget *TabWidget::findWidget(int x, int y) {
00357     if (y < _tabHeight) {
00358         if (_navButtonsVisible) {
00359             if (y >= _butTP && y < _butTP + _butH) {
00360                 if (x >= _w - _butRP - _butW * 2 - 2 && x < _w - _butRP - _butW - 2)
00361                     return _navLeft;
00362                 if (x >= _w - _butRP - _butW &&  x < _w - _butRP)
00363                     return _navRight;
00364             }
00365         }
00366 
00367         // Click was in the tab area
00368         return this;
00369     } else {
00370         // Iterate over all child widgets and find the one which was clicked
00371         return Widget::findWidgetInChain(_firstWidget, x, y - _tabHeight);
00372     }
00373 }
00374 
00375 void TabWidget::computeLastVisibleTab(bool adjustFirstIfRoom) {
00376     int availableWidth = _w;
00377     if (_navButtonsVisible)
00378         availableWidth -= 2 + _butW * 2;
00379 
00380     _lastVisibleTab = _tabs.size() - 1;
00381     for (int i = _firstVisibleTab; i < (int)_tabs.size(); ++i) {
00382         if (_tabs[i]._tabWidth > availableWidth) {
00383             if (i > _firstVisibleTab)
00384                 _lastVisibleTab = i - 1;
00385             else
00386                 _lastVisibleTab = _firstVisibleTab; // Always show 1
00387             break;
00388         }
00389         availableWidth -= _tabs[i]._tabWidth;
00390     }
00391 
00392     if (adjustFirstIfRoom) {
00393         // If possible, scroll to fit if there's unused space to the right
00394         while (_firstVisibleTab > 0 && _tabs[_firstVisibleTab-1]._tabWidth <= availableWidth) {
00395             availableWidth -= _tabs[_firstVisibleTab-1]._tabWidth;
00396             _firstVisibleTab--;
00397         }
00398     }
00399 }
00400 
00401 } // End of namespace GUI


Generated on Sat May 18 2019 05:01:22 for ResidualVM by doxygen 1.7.1
curved edge   curved edge