aboutsummaryrefslogtreecommitdiff
path: root/gui/widgets/tab.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gui/widgets/tab.cpp')
-rw-r--r--gui/widgets/tab.cpp320
1 files changed, 320 insertions, 0 deletions
diff --git a/gui/widgets/tab.cpp b/gui/widgets/tab.cpp
new file mode 100644
index 0000000000..48b4dab430
--- /dev/null
+++ b/gui/widgets/tab.cpp
@@ -0,0 +1,320 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/util.h"
+#include "gui/widgets/tab.h"
+#include "gui/dialog.h"
+#include "gui/GuiManager.h"
+
+#include "gui/ThemeEval.h"
+
+namespace GUI {
+
+enum {
+ kCmdLeft = 'LEFT',
+ kCmdRight = 'RGHT'
+};
+
+TabWidget::TabWidget(GuiObject *boss, int x, int y, int w, int h)
+ : Widget(boss, x, y, w, h), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
+ init();
+}
+
+TabWidget::TabWidget(GuiObject *boss, const String &name)
+ : Widget(boss, name), _bodyBackgroundType(GUI::ThemeEngine::kDialogBackgroundDefault) {
+ init();
+}
+
+void TabWidget::init() {
+ setFlags(WIDGET_ENABLED);
+ _type = kTabWidget;
+ _activeTab = -1;
+ _firstVisibleTab = 0;
+
+ _tabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
+ _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
+ _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
+
+ _bodyTP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Top");
+ _bodyBP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Bottom");
+ _bodyLP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Left");
+ _bodyRP = g_gui.xmlEval()->getVar("Globals.TabWidget.Body.Padding.Right");
+
+ _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButtonPadding.Right", 0);
+ _butTP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Padding.Top", 0);
+ _butW = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Width", 10);
+ _butH = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Height", 10);
+
+ int x = _w - _butRP - _butW * 2 - 2;
+ int y = _butTP - _tabHeight;
+ _navLeft = new ButtonWidget(this, x, y, _butW, _butH, "<", 0, kCmdLeft);
+ _navRight = new ButtonWidget(this, x + _butW + 2, y, _butW, _butH, ">", 0, kCmdRight);
+}
+
+TabWidget::~TabWidget() {
+ _firstWidget = 0;
+ for (uint i = 0; i < _tabs.size(); ++i) {
+ delete _tabs[i].firstWidget;
+ _tabs[i].firstWidget = 0;
+ }
+ _tabs.clear();
+ delete _navRight;
+}
+
+int16 TabWidget::getChildY() const {
+ return getAbsY() + _tabHeight;
+}
+
+int TabWidget::addTab(const String &title) {
+ // Add a new tab page
+ Tab newTab;
+ newTab.title = title;
+ newTab.firstWidget = 0;
+
+ _tabs.push_back(newTab);
+
+ int numTabs = _tabs.size();
+
+ // HACK: Nintendo DS uses a custom config dialog. This dialog does not work with
+ // our default "Globals.TabWidget.Tab.Width" setting.
+ //
+ // TODO: Add proper handling in the theme layout for such cases.
+ //
+ // There are different solutions to this problem:
+ // - offer a "Tab.Width" setting per tab widget and thus let the Ninteno DS
+ // backend set a default value for its special dialog.
+ //
+ // - change our themes to use auto width calculaction by default
+ //
+ // - change "Globals.TabWidget.Tab.Width" to be the minimal tab width setting and
+ // rename it accordingly.
+ // Actually this solution is pretty similar to our HACK for the Nintendo DS
+ // backend. This hack enables auto width calculation by default with the
+ // "Globals.TabWidget.Tab.Width" value as minimal width for the tab buttons.
+ //
+ // - we might also consider letting every tab button having its own width.
+ //
+ // - other solutions you can think of, which are hopefully less evil ;-).
+ //
+ // Of course also the Ninteno DS' dialog should be in our layouting engine, instead
+ // of being hard coded like it is right now.
+ //
+ // There are checks for __DS__ all over this source file to take care of the
+ // aforemnetioned problem.
+#ifdef __DS__
+ if (true) {
+#else
+ if (g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width") == 0) {
+#endif
+ if (_tabWidth == 0)
+ _tabWidth = 40;
+ // Determine the new tab width
+ int newWidth = g_gui.getStringWidth(title) + 2 * 3;
+ if (_tabWidth < newWidth)
+ _tabWidth = newWidth;
+ int maxWidth = _w / numTabs;
+ if (_tabWidth > maxWidth)
+ _tabWidth = maxWidth;
+ }
+
+ // Activate the new tab
+ setActiveTab(numTabs - 1);
+
+ return _activeTab;
+}
+
+void TabWidget::removeTab(int tabID) {
+ assert(0 <= tabID && tabID < (int)_tabs.size());
+
+ // Deactive the tab if it's currently the active one
+ if (tabID == _activeTab) {
+ _tabs[tabID].firstWidget = _firstWidget;
+ releaseFocus();
+ _firstWidget = 0;
+ }
+
+ // Dispose the widgets in that tab and then the tab itself
+ delete _tabs[tabID].firstWidget;
+ _tabs.remove_at(tabID);
+
+ // Adjust _firstVisibleTab if necessary
+ if (_firstVisibleTab >= (int)_tabs.size()) {
+ _firstVisibleTab = MAX(0, (int)_tabs.size() - 1);
+ }
+
+ // The active tab was removed, so select a new active one (if any remains)
+ if (tabID == _activeTab) {
+ _activeTab = -1;
+ if (tabID >= (int)_tabs.size())
+ tabID = _tabs.size() - 1;
+ if (tabID >= 0)
+ setActiveTab(tabID);
+ }
+
+ // Finally trigger a redraw
+ _boss->draw();
+}
+
+void TabWidget::setActiveTab(int tabID) {
+ assert(0 <= tabID && tabID < (int)_tabs.size());
+ if (_activeTab != tabID) {
+ // Exchange the widget lists, and switch to the new tab
+ if (_activeTab != -1) {
+ _tabs[_activeTab].firstWidget = _firstWidget;
+ releaseFocus();
+ }
+ _activeTab = tabID;
+ _firstWidget = _tabs[tabID].firstWidget;
+ _boss->draw();
+ }
+}
+
+
+void TabWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
+ Widget::handleCommand(sender, cmd, data);
+
+ switch (cmd) {
+ case kCmdLeft:
+ if (_firstVisibleTab) {
+ _firstVisibleTab--;
+ draw();
+ }
+ break;
+
+ case kCmdRight:
+ if (_firstVisibleTab + _w / _tabWidth < (int)_tabs.size()) {
+ _firstVisibleTab++;
+ draw();
+ }
+ break;
+ }
+}
+
+void TabWidget::handleMouseDown(int x, int y, int button, int clickCount) {
+ assert(y < _tabHeight);
+
+ // Determine which tab was clicked
+ int tabID = -1;
+ if (x >= 0 && (x % _tabWidth) < _tabWidth) {
+ tabID = x / _tabWidth;
+ if (tabID >= (int)_tabs.size())
+ tabID = -1;
+ }
+
+ // If a tab was clicked, switch to that pane
+ if (tabID >= 0 && tabID + _firstVisibleTab < (int)_tabs.size()) {
+ setActiveTab(tabID + _firstVisibleTab);
+ }
+}
+
+bool TabWidget::handleKeyDown(Common::KeyState state) {
+ // TODO: maybe there should be a way to switch between tabs
+ // using the keyboard? E.g. Alt-Shift-Left/Right-Arrow or something
+ // like that.
+ return Widget::handleKeyDown(state);
+}
+
+void TabWidget::reflowLayout() {
+ Widget::reflowLayout();
+
+ for (uint i = 0; i < _tabs.size(); ++i) {
+ Widget *w = _tabs[i].firstWidget;
+ while (w) {
+ w->reflowLayout();
+ w = w->next();
+ }
+ }
+
+ _tabHeight = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Height");
+ _tabWidth = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Width");
+ _titleVPad = g_gui.xmlEval()->getVar("Globals.TabWidget.Tab.Padding.Top");
+
+ if (_tabWidth == 0) {
+ _tabWidth = 40;
+#ifdef __DS__
+ }
+ if (true) {
+#endif
+ int maxWidth = _w / _tabs.size();
+
+ for (uint i = 0; i < _tabs.size(); ++i) {
+ // Determine the new tab width
+ int newWidth = g_gui.getStringWidth(_tabs[i].title) + 2 * 3;
+ if (_tabWidth < newWidth)
+ _tabWidth = newWidth;
+ if (_tabWidth > maxWidth)
+ _tabWidth = maxWidth;
+ }
+ }
+
+ _butRP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.PaddingRight", 0);
+ _butTP = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Padding.Top", 0);
+ _butW = g_gui.xmlEval()->getVar("GlobalsTabWidget.NavButton.Width", 10);
+ _butH = g_gui.xmlEval()->getVar("Globals.TabWidget.NavButton.Height", 10);
+
+ int x = _w - _butRP - _butW * 2 - 2;
+ int y = _butTP - _tabHeight;
+ _navLeft->resize(x, y, _butW, _butH);
+ _navRight->resize(x + _butW + 2, y, _butW, _butH);
+}
+
+void TabWidget::drawWidget() {
+ Common::Array<Common::String> tabs;
+ for (int i = _firstVisibleTab; i < (int)_tabs.size(); ++i) {
+ tabs.push_back(_tabs[i].title);
+ }
+ g_gui.theme()->drawDialogBackground(Common::Rect(_x + _bodyLP, _y + _bodyTP, _x+_w-_bodyRP, _y+_h-_bodyBP), _bodyBackgroundType);
+
+ g_gui.theme()->drawTab(Common::Rect(_x, _y, _x+_w, _y+_h), _tabHeight, _tabWidth, tabs, _activeTab - _firstVisibleTab, 0, _titleVPad);
+}
+
+void TabWidget::draw() {
+ Widget::draw();
+
+ if (_tabWidth * _tabs.size() > _w) {
+ _navLeft->draw();
+ _navRight->draw();
+ }
+}
+
+Widget *TabWidget::findWidget(int x, int y) {
+ if (y < _tabHeight) {
+ if (_tabWidth * _tabs.size() > _w) {
+ if (y >= _butTP && y < _butTP + _butH) {
+ if (x >= _w - _butRP - _butW * 2 - 2 && x < _w - _butRP - _butW - 2)
+ return _navLeft;
+ if (x >= _w - _butRP - _butW && x < _w - _butRP)
+ return _navRight;
+ }
+ }
+
+ // Click was in the tab area
+ return this;
+ } else {
+ // Iterate over all child widgets and find the one which was clicked
+ return Widget::findWidgetInChain(_firstWidget, x, y - _tabHeight);
+ }
+}
+
+} // End of namespace GUI