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


Generated on Sat May 23 2020 05:00:44 for ResidualVM by doxygen 1.7.1
curved edge   curved edge