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

ThemeLayout.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 "common/system.h"
00025 
00026 #include "gui/gui-manager.h"
00027 #include "gui/widget.h"
00028 #include "gui/ThemeEval.h"
00029 #include "gui/ThemeLayout.h"
00030 
00031 #include "graphics/font.h"
00032 
00033 #ifdef LAYOUT_DEBUG_DIALOG
00034 #include "graphics/surface.h"
00035 #endif
00036 
00037 namespace GUI {
00038 
00039 void ThemeLayout::importLayout(ThemeLayout *layout) {
00040     assert(layout->getLayoutType() == kLayoutMain);
00041 
00042     if (layout->_children.size() == 0)
00043         return;
00044 
00045     layout = layout->_children[0];
00046 
00047     if (getLayoutType() == layout->getLayoutType()) {
00048         for (uint i = 0; i < layout->_children.size(); ++i)
00049             _children.push_back(layout->_children[i]->makeClone(this));
00050     } else {
00051         ThemeLayout *clone = layout->makeClone(this);
00052 
00053         // When importing a layout into a layout of the same type, the children
00054         // of the imported layout are copied over, ignoring the padding of the
00055         // imported layout. Here when importing a layout of a different type
00056         // into a layout we explicitly ignore the padding so the appearance
00057         // is the same in both cases.
00058         clone->setPadding(0, 0, 0, 0);
00059 
00060         _children.push_back(clone);
00061     }
00062 }
00063 
00064 void ThemeLayout::resetLayout() {
00065     _x = 0;
00066     _y = 0;
00067     _w = _defaultW;
00068     _h = _defaultH;
00069 
00070     for (uint i = 0; i < _children.size(); ++i)
00071         _children[i]->resetLayout();
00072 }
00073 
00074 bool ThemeLayout::getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL) {
00075     if (name.empty()) {
00076         assert(getLayoutType() == kLayoutMain);
00077         x = _x; y = _y;
00078         w = _w; h = _h;
00079         useRTL = _useRTL;
00080 
00081         return true;
00082     }
00083 
00084     for (uint i = 0; i < _children.size(); ++i) {
00085         if (_children[i]->getWidgetData(name, x, y, w, h, useRTL))
00086             return true;
00087     }
00088 
00089     return false;
00090 }
00091 
00092 Graphics::TextAlign ThemeLayout::getWidgetTextHAlign(const Common::String &name) {
00093     if (name.empty()) {
00094         assert(getLayoutType() == kLayoutMain);
00095         return _textHAlign;
00096     }
00097 
00098     Graphics::TextAlign res;
00099 
00100     for (uint i = 0; i < _children.size(); ++i) {
00101         if ((res = _children[i]->getWidgetTextHAlign(name)) != Graphics::kTextAlignInvalid)
00102             return res;
00103     }
00104 
00105     return Graphics::kTextAlignInvalid;
00106 }
00107 
00108 int16 ThemeLayoutStacked::getParentWidth() {
00109     ThemeLayout *p = _parent;
00110     int width = 0;
00111 
00112     while (p && p->getLayoutType() != kLayoutMain) {
00113         width += p->_padding.right + p->_padding.left;
00114         if (p->getLayoutType() == kLayoutHorizontal) {
00115             const int spacing = ((ThemeLayoutStacked *)p)->_spacing;
00116             for (uint i = 0; i < p->_children.size(); ++i)
00117                 width += p->_children[i]->getWidth() + spacing;
00118         }
00119         // FIXME: Do we really want to assume that any layout type different
00120         // from kLayoutHorizontal corresponds to width 0 ?
00121         p = p->_parent;
00122     }
00123 
00124     assert(p && p->getLayoutType() == kLayoutMain);
00125     return p->getWidth() - width;
00126 }
00127 
00128 int16 ThemeLayoutStacked::getParentHeight() {
00129     ThemeLayout *p = _parent;
00130     int height = 0;
00131 
00132     while (p && p->getLayoutType() != kLayoutMain) {
00133         height += p->_padding.bottom + p->_padding.top;
00134         if (p->getLayoutType() == kLayoutVertical) {
00135             const int spacing = ((ThemeLayoutStacked *)p)->_spacing;
00136             for (uint i = 0; i < p->_children.size(); ++i)
00137                 height += p->_children[i]->getHeight() + spacing;
00138         }
00139         // FIXME: Do we really want to assume that any layout type different
00140         // from kLayoutVertical corresponds to height 0 ?
00141         p = p->_parent;
00142     }
00143 
00144     assert(p && p->getLayoutType() == kLayoutMain);
00145     return p->getHeight() - height;
00146 }
00147 
00148 #ifdef LAYOUT_DEBUG_DIALOG
00149 void ThemeLayout::debugDraw(Graphics::Surface *screen, const Graphics::Font *font) {
00150     uint32 color = 0xFFFFFFFF;
00151     font->drawString(screen, getName(), _x, _y, _w, color, Graphics::kTextAlignRight, 0, true);
00152     screen->hLine(_x, _y, _x + _w, color);
00153     screen->hLine(_x, _y + _h, _x + _w , color);
00154     screen->vLine(_x, _y, _y + _h, color);
00155     screen->vLine(_x + _w, _y, _y + _h, color);
00156 
00157     for (uint i = 0; i < _children.size(); ++i)
00158         _children[i]->debugDraw(screen, font);
00159 }
00160 #endif
00161 
00162 
00163 bool ThemeLayoutWidget::getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL) {
00164     if (name == _name) {
00165         x = _x; y = _y;
00166         w = _w; h = _h;
00167         useRTL = _useRTL;
00168 
00169         return true;
00170     }
00171 
00172     return false;
00173 }
00174 
00175 Graphics::TextAlign ThemeLayoutWidget::getWidgetTextHAlign(const Common::String &name) {
00176     if (name == _name) {
00177         return _textHAlign;
00178     }
00179 
00180     return Graphics::kTextAlignInvalid;
00181 }
00182 
00183 void ThemeLayoutWidget::reflowLayout(Widget *widgetChain) {
00184     Widget *guiWidget = getWidget(widgetChain);
00185     if (!guiWidget) {
00186         return;
00187     }
00188 
00189     int minWidth  = -1;
00190     int minHeight = -1;
00191     guiWidget->getMinSize(minWidth, minHeight);
00192 
00193     if (_w != -1 && minWidth != -1 && minWidth > _w) {
00194         _w = minWidth;
00195     }
00196 
00197     if (_h != -1 && minHeight != -1 && minHeight > _h) {
00198         _h = minHeight;
00199     }
00200 }
00201 
00202 bool ThemeLayoutWidget::isBound(Widget *widgetChain) const {
00203     Widget *guiWidget = getWidget(widgetChain);
00204     return guiWidget != nullptr;
00205 }
00206 
00207 Widget *ThemeLayoutWidget::getWidget(Widget *widgetChain) const {
00208     const ThemeLayout *topLevelLayout = this;
00209     while (topLevelLayout->_parent) {
00210         topLevelLayout = topLevelLayout->_parent;
00211     }
00212 
00213     assert(topLevelLayout && topLevelLayout->getLayoutType() == kLayoutMain);
00214     const ThemeLayoutMain *dialogLayout = static_cast<const ThemeLayoutMain *>(topLevelLayout);
00215 
00216     Common::String widgetName = Common::String::format("%s.%s", dialogLayout->getName(), _name.c_str());
00217     return Widget::findWidgetInChain(widgetChain, widgetName.c_str());
00218 }
00219 
00220 void ThemeLayoutMain::reflowLayout(Widget *widgetChain) {
00221     assert(_children.size() <= 1);
00222 
00223     resetLayout();
00224 
00225     if (_overlays == "screen") {
00226         _x = 0;
00227         _y = 0;
00228         _w = g_system->getOverlayWidth();
00229         _h = g_system->getOverlayHeight();
00230     } else if (_overlays == "screen_center") {
00231         _x = -1;
00232         _y = -1;
00233         _w = _defaultW > 0 ? MIN(_defaultW, g_system->getOverlayWidth()) : -1;
00234         _h = _defaultH > 0 ? MIN(_defaultH, g_system->getOverlayHeight()) : -1;
00235     } else {
00236         if (!g_gui.xmlEval()->getWidgetData(_overlays, _x, _y, _w, _h)) {
00237             warning("Unable to retrieve overlayed dialog position %s", _overlays.c_str());
00238         }
00239 
00240         if (_w == -1 || _h == -1) {
00241             warning("The overlayed dialog %s has not been sized, using a default size for %s", _overlays.c_str(), _name.c_str());
00242             _x = g_system->getOverlayWidth()      / 10;
00243             _y = g_system->getOverlayHeight()     / 10;
00244             _w = g_system->getOverlayWidth()  * 8 / 10;
00245             _h = g_system->getOverlayHeight() * 8 / 10;
00246         }
00247     }
00248 
00249     if (g_gui.useRTL()) {
00250         if (this->_name == "GameOptions" || this->_name == "GlobalOptions" || this->_name == "Browser") {
00256             int oldX = _x;
00257             _x = g_system->getOverlayWidth() - _w - _x;
00258             g_gui.setDialogPaddings(oldX, _x);
00259         }
00260     }
00261 
00262     if (_x >= 0) _x += _inset;
00263     if (_y >= 0) _y += _inset;
00264     if (_w >= 0) _w -= 2 * _inset;
00265     if (_h >= 0) _h -= 2 * _inset;
00266 
00267     if (_children.size()) {
00268         _children[0]->setWidth(_w);
00269         _children[0]->setHeight(_h);
00270         _children[0]->reflowLayout(widgetChain);
00271 
00272         if (_w == -1)
00273             _w = _children[0]->getWidth();
00274 
00275         if (_h == -1)
00276             _h = _children[0]->getHeight();
00277 
00278         if (_y == -1)
00279             _y = (g_system->getOverlayHeight() >> 1) - (_h >> 1);
00280 
00281         if (_x == -1)
00282             _x = (g_system->getOverlayWidth() >> 1) - (_w >> 1);
00283     }
00284 }
00285 
00286 void ThemeLayoutStacked::reflowLayoutVertical(Widget *widgetChain) {
00287     int curY;
00288     int resize[8];
00289     int rescount = 0;
00290     bool fixedWidth = _w != -1;
00291 
00292     curY = _padding.top;
00293     _h = _padding.top + _padding.bottom;
00294 
00295     for (uint i = 0; i < _children.size(); ++i) {
00296         if (!_children[i]->isBound(widgetChain)) continue;
00297 
00298         _children[i]->reflowLayout(widgetChain);
00299 
00300         if (_children[i]->getWidth() == -1) {
00301             int16 width = (_w == -1 ? getParentWidth() : _w) - _padding.left - _padding.right;
00302             _children[i]->setWidth(MAX<int16>(width, 0));
00303         }
00304 
00305         if (_children[i]->getHeight() == -1) {
00306             assert(rescount < ARRAYSIZE(resize));
00307             resize[rescount++] = i;
00308             _children[i]->setHeight(0);
00309         }
00310 
00311         _children[i]->offsetY(curY);
00312 
00313         // Advance the vertical offset by the height of the newest item, plus
00314         // the item spacing value.
00315         curY += _children[i]->getHeight() + _spacing;
00316 
00317         // Update width and height of this stack layout
00318         if (!fixedWidth) {
00319             _w = MAX(_w, (int16)(_children[i]->getWidth() + _padding.left + _padding.right));
00320         }
00321         _h += _children[i]->getHeight() + _spacing;
00322     }
00323 
00324     // If there are any children at all, then we added the spacing value once
00325     // too often. Correct that.
00326     if (!_children.empty())
00327         _h -= _spacing;
00328 
00329     // If the width is not set at this point, then we have no bound widgets.
00330     if (!fixedWidth && _w == -1) {
00331         _w = 0;
00332     }
00333 
00334     for (uint i = 0; i < _children.size(); ++i) {
00335         switch (_itemAlign) {
00336         case kItemAlignStart:
00337         default:
00338             _children[i]->offsetX(_padding.left);
00339             break;
00340         case kItemAlignCenter:
00341             // Center child if it this has been requested *and* the space permits it.
00342             if (_children[i]->getWidth() < (_w - _padding.left - _padding.right)) {
00343                 _children[i]->offsetX((_w >> 1) - (_children[i]->getWidth() >> 1));
00344             } else {
00345                 _children[i]->offsetX(_padding.left);
00346             }
00347             break;
00348         case kItemAlignEnd:
00349             _children[i]->offsetX(_w - _children[i]->getWidth() - _padding.right);
00350             break;
00351         case kItemAlignStretch:
00352             _children[i]->offsetX(_padding.left);
00353             _children[i]->setWidth(_w - _padding.left - _padding.right);
00354             break;
00355         }
00356     }
00357 
00358     // If there were any items with undetermined height, then compute and set
00359     // their height now. We do so by determining how much space is left, and
00360     // then distributing this equally over all items which need auto-resizing.
00361     if (rescount) {
00362         int newh = (getParentHeight() - _h - _padding.bottom) / rescount;
00363         if (newh < 0) newh = 0; // In case there is no room left, avoid giving a negative height to widgets
00364 
00365         for (int i = 0; i < rescount; ++i) {
00366             // Set the height of the item.
00367             _children[resize[i]]->setHeight(newh);
00368             // Increase the height of this ThemeLayoutStacked accordingly, and
00369             // then shift all subsequence children.
00370             _h += newh;
00371             for (uint j = resize[i] + 1; j < _children.size(); ++j)
00372                 _children[j]->offsetY(newh);
00373         }
00374     }
00375 }
00376 
00377 void ThemeLayoutStacked::reflowLayoutHorizontal(Widget *widgetChain) {
00378     int curX;
00379     int resize[8];
00380     int rescount = 0;
00381     bool fixedHeight = _h != -1;
00382 
00383     curX = _padding.left;
00384     _w = _padding.left + _padding.right;
00385 
00386     for (uint i = 0; i < _children.size(); ++i) {
00387         if (!_children[i]->isBound(widgetChain)) continue;
00388 
00389         _children[i]->reflowLayout(widgetChain);
00390 
00391         if (_children[i]->getHeight() == -1) {
00392             int16 height = (_h == -1 ? getParentHeight() : _h) - _padding.top - _padding.bottom;
00393             _children[i]->setHeight(MAX<int16>(height, 0));
00394         }
00395 
00396         if (_children[i]->getWidth() == -1) {
00397             assert(rescount < ARRAYSIZE(resize));
00398             resize[rescount++] = i;
00399             _children[i]->setWidth(0);
00400         }
00401 
00402         _children[i]->offsetX(curX);
00403 
00404         // Advance the horizontal offset by the width of the newest item, plus
00405         // the item spacing value.
00406         curX += (_children[i]->getWidth() + _spacing);
00407 
00408         // Update width and height of this stack layout
00409         _w += _children[i]->getWidth() + _spacing;
00410         if (!fixedHeight) {
00411             _h = MAX(_h, (int16)(_children[i]->getHeight() + _padding.top + _padding.bottom));
00412         }
00413     }
00414 
00415     // If there are any children at all, then we added the spacing value once
00416     // too often. Correct that.
00417     if (!_children.empty())
00418         _w -= _spacing;
00419 
00420     // If the height is not set at this point, then we have no bound widgets.
00421     if (!fixedHeight && _h == -1) {
00422         _h = 0;
00423     }
00424 
00425     for (uint i = 0; i < _children.size(); ++i) {
00426         switch (_itemAlign) {
00427         case kItemAlignStart:
00428         default:
00429             _children[i]->offsetY(_padding.top);
00430             break;
00431         case kItemAlignCenter:
00432             // Center child if it this has been requested *and* the space permits it.
00433             if (_children[i]->getHeight() < (_h - _padding.top - _padding.bottom)) {
00434                 _children[i]->offsetY((_h >> 1) - (_children[i]->getHeight() >> 1));
00435             } else {
00436                 _children[i]->offsetY(_padding.top);
00437             }
00438             break;
00439         case kItemAlignEnd:
00440             _children[i]->offsetY(_h - _children[i]->getHeight() - _padding.bottom);
00441             break;
00442         case kItemAlignStretch:
00443             _children[i]->offsetY(_padding.top);
00444             _children[i]->setHeight(_w - _padding.top - _padding.bottom);
00445             break;
00446         }
00447     }
00448 
00449     // If there were any items with undetermined width, then compute and set
00450     // their width now. We do so by determining how much space is left, and
00451     // then distributing this equally over all items which need auto-resizing.
00452     if (rescount) {
00453         int neww = (getParentWidth() - _w - _padding.right) / rescount;
00454         if (neww < 0) neww = 0; // In case there is no room left, avoid giving a negative width to widgets
00455 
00456         for (int i = 0; i < rescount; ++i) {
00457             // Set the width of the item.
00458             _children[resize[i]]->setWidth(neww);
00459             // Increase the width of this ThemeLayoutStacked accordingly, and
00460             // then shift all subsequence children.
00461             _w += neww;
00462             for (uint j = resize[i] + 1; j < _children.size(); ++j)
00463                 _children[j]->offsetX(neww);
00464         }
00465     }
00466 }
00467 
00468 } // End of namespace GUI


Generated on Sat Sep 12 2020 05:01:47 for ResidualVM by doxygen 1.7.1
curved edge   curved edge