1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef THEME_LAYOUT_H
24 #define THEME_LAYOUT_H
25 
26 #include "common/array.h"
27 #include "common/rect.h"
28 #include "graphics/font.h"
29 
30 #ifdef LAYOUT_DEBUG_DIALOG
31 namespace Graphics {
32 struct Surface;
33 }
34 #endif
35 
36 namespace GUI {
37 
38 class Widget;
39 
40 class ThemeLayout {
41 	friend class ThemeLayoutMain;
42 	friend class ThemeLayoutStacked;
43 	friend class ThemeLayoutSpacing;
44 	friend class ThemeLayoutWidget;
45 public:
46 	enum LayoutType {
47 		kLayoutMain,
48 		kLayoutVertical,
49 		kLayoutHorizontal,
50 		kLayoutWidget,
51 		kLayoutTabWidget,
52 		kLayoutSpace
53 	};
54 
55 	/// Cross-direction alignment of layout children.
56 	enum ItemAlign {
57 		kItemAlignStart,   ///< Items are aligned to the left for vertical layouts or to the top for horizontal layouts
58 		kItemAlignCenter,  ///< Items are centered in the container
59 		kItemAlignEnd,     ///< Items are aligned to the right for vertical layouts or to the bottom for horizontal layouts
60 		kItemAlignStretch  ///< Items are resized to match the size of the layout in the cross-direction
61 	};
62 
ThemeLayout(ThemeLayout * p)63 	ThemeLayout(ThemeLayout *p) :
64 		_parent(p), _x(0), _y(0), _w(-1), _h(-1),
65 		_defaultW(-1), _defaultH(-1),
66 		_textHAlign(Graphics::kTextAlignInvalid), _useRTL(true) {}
67 
~ThemeLayout()68 	virtual ~ThemeLayout() {
69 		for (uint i = 0; i < _children.size(); ++i)
70 			delete _children[i];
71 	}
72 
73 	virtual void reflowLayout(Widget *widgetChain) = 0;
74 	virtual void resetLayout();
75 
addChild(ThemeLayout * child)76 	void addChild(ThemeLayout *child) { _children.push_back(child); }
77 
setPadding(int8 left,int8 right,int8 top,int8 bottom)78 	void setPadding(int8 left, int8 right, int8 top, int8 bottom) {
79 		_padding.left = left;
80 		_padding.right = right;
81 		_padding.top = top;
82 		_padding.bottom = bottom;
83 	}
84 
85 protected:
getWidth()86 	int16 getWidth() { return _w; }
getHeight()87 	int16 getHeight() { return _h; }
88 
offsetX(int newX)89 	void offsetX(int newX) {
90 		_x += newX;
91 		for (uint i = 0; i < _children.size(); ++i)
92 			_children[i]->offsetX(newX);
93 	}
94 
offsetY(int newY)95 	void offsetY(int newY) {
96 		_y += newY;
97 		for (uint i = 0; i < _children.size(); ++i)
98 			_children[i]->offsetY(newY);
99 	}
100 
setWidth(int16 width)101 	void setWidth(int16 width) { _w = width; }
setHeight(int16 height)102 	void setHeight(int16 height) { _h = height; }
setTextHAlign(Graphics::TextAlign align)103 	void setTextHAlign(Graphics::TextAlign align) { _textHAlign = align; }
104 
105 	/**
106 	 * Checks if the layout element is attached to a GUI widget
107 	 *
108 	 * Layout elements that are not bound do not take space.
109 	 */
isBound(Widget * widgetChain)110 	virtual bool isBound(Widget *widgetChain) const { return true; }
111 
112 	virtual LayoutType getLayoutType() const = 0;
113 
114 	virtual ThemeLayout *makeClone(ThemeLayout *newParent) = 0;
115 
116 public:
117 	virtual bool getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL);
getUseRTL()118 	bool getUseRTL() { return _useRTL; }
119 
120 	virtual Graphics::TextAlign getWidgetTextHAlign(const Common::String &name);
121 
122 	void importLayout(ThemeLayout *layout);
123 
getTextHAlign()124 	Graphics::TextAlign getTextHAlign() { return _textHAlign; }
125 
126 #ifdef LAYOUT_DEBUG_DIALOG
127 	void debugDraw(Graphics::Surface *screen, const Graphics::Font *font);
128 
129 	virtual const char *getName() const = 0;
130 #endif
131 
132 protected:
133 	ThemeLayout *_parent;
134 	int16 _x, _y, _w, _h;
135 	bool _useRTL;
136 	Common::Rect _padding;
137 	Common::Array<ThemeLayout *> _children;
138 	int16 _defaultW, _defaultH;
139 	Graphics::TextAlign _textHAlign;
140 };
141 
142 class ThemeLayoutMain : public ThemeLayout {
143 public:
ThemeLayoutMain(const Common::String & name,const Common::String & overlays,int16 width,int16 height,int inset)144 	ThemeLayoutMain(const Common::String &name, const Common::String &overlays, int16 width, int16 height, int inset) :
145 			ThemeLayout(nullptr),
146 			_name(name),
147 			_overlays(overlays),
148 			_inset(inset) {
149 		_w = _defaultW = width;
150 		_h = _defaultH = height;
151 		_x = _defaultX = -1;
152 		_y = _defaultY = -1;
153 	}
154 	void reflowLayout(Widget *widgetChain) override;
155 
resetLayout()156 	void resetLayout() override {
157 		ThemeLayout::resetLayout();
158 		_x = _defaultX;
159 		_y = _defaultY;
160 	}
161 
getName()162 	const char *getName() const { return _name.c_str(); }
163 
164 protected:
getLayoutType()165 	LayoutType getLayoutType() const override { return kLayoutMain; }
makeClone(ThemeLayout * newParent)166 	ThemeLayout *makeClone(ThemeLayout *newParent) override { assert(!"Do not copy Main Layouts!"); return nullptr; }
167 
168 	int16 _defaultX;
169 	int16 _defaultY;
170 
171 	Common::String _name;
172 	Common::String _overlays;
173 	int _inset;
174 };
175 
176 class ThemeLayoutStacked : public ThemeLayout {
177 public:
ThemeLayoutStacked(ThemeLayout * p,LayoutType type,int spacing,ItemAlign itemAlign)178 	ThemeLayoutStacked(ThemeLayout *p, LayoutType type, int spacing, ItemAlign itemAlign) :
179 		ThemeLayout(p), _type(type), _itemAlign(itemAlign) {
180 		assert((type == kLayoutVertical) || (type == kLayoutHorizontal));
181 		_spacing = spacing;
182 	}
183 
reflowLayout(Widget * widgetChain)184 	void reflowLayout(Widget *widgetChain) override {
185 		if (_type == kLayoutVertical)
186 			reflowLayoutVertical(widgetChain);
187 		else
188 			reflowLayoutHorizontal(widgetChain);
189 	}
190 
191 	void reflowLayoutHorizontal(Widget *widgetChain);
192 	void reflowLayoutVertical(Widget *widgetChain);
193 
194 #ifdef LAYOUT_DEBUG_DIALOG
getName()195 	const char *getName() const {
196 		return (_type == kLayoutVertical)
197 			? "Vertical Layout" : "Horizontal Layout";
198 	}
199 #endif
200 
201 protected:
202 	int16 getParentWidth();
203 	int16 getParentHeight();
204 
getLayoutType()205 	LayoutType getLayoutType() const override { return _type; }
206 
makeClone(ThemeLayout * newParent)207 	ThemeLayout *makeClone(ThemeLayout *newParent) override {
208 		ThemeLayoutStacked *n = new ThemeLayoutStacked(*this);
209 		n->_parent = newParent;
210 
211 		for (uint i = 0; i < n->_children.size(); ++i)
212 			n->_children[i] = n->_children[i]->makeClone(n);
213 
214 		return n;
215 	}
216 
217 	const LayoutType _type;
218 	ItemAlign _itemAlign;
219 	int8 _spacing;
220 };
221 
222 class ThemeLayoutWidget : public ThemeLayout {
223 public:
ThemeLayoutWidget(ThemeLayout * p,const Common::String & name,int16 w,int16 h,Graphics::TextAlign align,bool useRTL)224 	ThemeLayoutWidget(ThemeLayout *p, const Common::String &name, int16 w, int16 h, Graphics::TextAlign align, bool useRTL) : ThemeLayout(p), _name(name) {
225 		_w = _defaultW = w;
226 		_h = _defaultH = h;
227 		_useRTL = useRTL;
228 
229 		setTextHAlign(align);
230 	}
231 
232 	bool getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL) override;
233 	Graphics::TextAlign getWidgetTextHAlign(const Common::String &name) override;
234 
235 	void reflowLayout(Widget *widgetChain) override;
236 
getName()237 	virtual const char *getName() const { return _name.c_str(); }
238 
239 protected:
getLayoutType()240 	LayoutType getLayoutType() const override { return kLayoutWidget; }
241 
242 	bool isBound(Widget *widgetChain) const override;
243 	Widget *getWidget(Widget *widgetChain) const;
244 
makeClone(ThemeLayout * newParent)245 	ThemeLayout *makeClone(ThemeLayout *newParent) override {
246 		ThemeLayout *n = new ThemeLayoutWidget(*this);
247 		n->_parent = newParent;
248 		return n;
249 	}
250 
251 	Common::String _name;
252 };
253 
254 class ThemeLayoutTabWidget : public ThemeLayoutWidget {
255 	int _tabHeight;
256 
257 public:
ThemeLayoutTabWidget(ThemeLayout * p,const Common::String & name,int16 w,int16 h,Graphics::TextAlign align,int tabHeight)258 	ThemeLayoutTabWidget(ThemeLayout *p, const Common::String &name, int16 w, int16 h, Graphics::TextAlign align, int tabHeight):
259 		ThemeLayoutWidget(p, name, w, h, align, p->getUseRTL()) {
260 		_tabHeight = tabHeight;
261 	}
262 
reflowLayout(Widget * widgetChain)263 	void reflowLayout(Widget *widgetChain) override {
264 		for (uint i = 0; i < _children.size(); ++i) {
265 			_children[i]->reflowLayout(widgetChain);
266 		}
267 	}
268 
getWidgetData(const Common::String & name,int16 & x,int16 & y,int16 & w,int16 & h,bool & useRTL)269 	bool getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL) override {
270 		if (ThemeLayoutWidget::getWidgetData(name, x, y, w, h, useRTL)) {
271 			h -= _tabHeight;
272 			return true;
273 		}
274 
275 		return false;
276 	}
277 
278 protected:
getLayoutType()279 	LayoutType getLayoutType() const override { return kLayoutTabWidget; }
280 
makeClone(ThemeLayout * newParent)281 	ThemeLayout *makeClone(ThemeLayout *newParent) override {
282 		ThemeLayoutTabWidget *n = new ThemeLayoutTabWidget(*this);
283 		n->_parent = newParent;
284 		return n;
285 	}
286 };
287 
288 class ThemeLayoutSpacing : public ThemeLayout {
289 public:
ThemeLayoutSpacing(ThemeLayout * p,int size)290 	ThemeLayoutSpacing(ThemeLayout *p, int size) : ThemeLayout(p) {
291 		if (p->getLayoutType() == kLayoutHorizontal) {
292 			_w = _defaultW = size;
293 			_h = _defaultH = 1;
294 		} else if (p->getLayoutType() == kLayoutVertical) {
295 			_w = _defaultW = 1;
296 			_h = _defaultH = size;
297 		}
298 	}
299 
getWidgetData(const Common::String & name,int16 & x,int16 & y,int16 & w,int16 & h,bool & useRTL)300 	bool getWidgetData(const Common::String &name, int16 &x, int16 &y, int16 &w, int16 &h, bool &useRTL) override { return false; }
reflowLayout(Widget * widgetChain)301 	void reflowLayout(Widget *widgetChain) override {}
302 #ifdef LAYOUT_DEBUG_DIALOG
getName()303 	const char *getName() const { return "SPACE"; }
304 #endif
305 
306 protected:
getLayoutType()307 	LayoutType getLayoutType() const override { return kLayoutSpace; }
308 
makeClone(ThemeLayout * newParent)309 	ThemeLayout *makeClone(ThemeLayout *newParent) override {
310 		ThemeLayout *n = new ThemeLayoutSpacing(*this);
311 		n->_parent = newParent;
312 		return n;
313 	}
314 };
315 
316 }
317 
318 #endif
319