1 /*
2  *  This file is part of Dune Legacy.
3  *
4  *  Dune Legacy is free software: you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation, either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  Dune Legacy is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with Dune Legacy.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef HBOX_H
19 #define HBOX_H
20 
21 #include "Container.h"
22 
23 #include <algorithm>
24 
25 class HBox_WidgetData {
26 public:
HBox_WidgetData()27     HBox_WidgetData() : pWidget(nullptr), fixedWidth(0), weight(0.0) { };
HBox_WidgetData(Widget * _pWidget,Sint32 _fixedWidth)28     HBox_WidgetData(Widget* _pWidget, Sint32 _fixedWidth) : pWidget(_pWidget), fixedWidth(_fixedWidth), weight(0.0) { };
HBox_WidgetData(Widget * _pWidget,double _weight)29     HBox_WidgetData(Widget* _pWidget, double _weight) : pWidget(_pWidget), fixedWidth(-1), weight(_weight) { };
30 
31     Widget* pWidget;
32     Sint32 fixedWidth;
33     double weight;
34 };
35 
36 /// A container class for horizontal aligned widgets.
37 class HBox : public Container<HBox_WidgetData> {
38 public:
39     /// default constructor
HBox()40     HBox() : Container<HBox_WidgetData>() {
41         ;
42     }
43 
44     /// destructor
~HBox()45     virtual ~HBox() {
46         ;
47     }
48 
49     /**
50         This method adds a new widget to this container.
51         \param newWidget    Widget to add
52         \param fixedWidth   a fixed width for this widget (must be greater than the minimum size)
53     */
addWidget(Widget * newWidget,Sint32 fixedWidth)54     virtual void addWidget(Widget* newWidget, Sint32 fixedWidth) {
55         if(newWidget != nullptr) {
56             containedWidgets.push_back(HBox_WidgetData(newWidget, fixedWidth));
57             newWidget->setParent(this);
58             Widget::resizeAll();
59         }
60     }
61 
62     /**
63         This method adds a new widget to this container.
64         \param newWidget    Widget to add
65         \param weight       The weight for this widget (default=1.0)
66     */
67     virtual void addWidget(Widget* newWidget, double weight = 1.0) {
68         if(newWidget != nullptr) {
69             containedWidgets.push_back(HBox_WidgetData(newWidget, weight));
70             newWidget->setParent(this);
71             Widget::resizeAll();
72         }
73     }
74 
75     /**
76         Returns the minimum size of this container. The container should not
77         resized to a size smaller than this. If the container is not resizeable
78         in a direction this method returns the size in that direction.
79         \return the minimum size of this container
80     */
getMinimumSize()81     virtual Point getMinimumSize() const {
82         Point p(0,0);
83         for(const HBox_WidgetData& widgetData : containedWidgets) {
84             if(widgetData.fixedWidth > 0) {
85                 p.x += widgetData.fixedWidth;
86             } else {
87                 p.x += widgetData.pWidget->getMinimumSize().x;
88             }
89             p.y = std::max(p.y,widgetData.pWidget->getMinimumSize().y);
90         }
91         return p;
92     }
93 
94     /**
95         This method resizes the container. This method should only
96         called if the new size is a valid size for this container (See getMinumumSize).
97         \param  newSize the new size of this progress bar
98     */
resize(Point newSize)99     virtual void resize(Point newSize) {
100         resize(newSize.x,newSize.y);
101     }
102 
103     /**
104         This method resizes the container to width and height. This method should only be
105         called if the new size is a valid size for this container (See resizingXAllowed,
106         resizingYAllowed, getMinumumSize). It also resizes all child widgets.
107         \param  width   the new width of this container
108         \param  height  the new height of this container
109     */
resize(Uint32 width,Uint32 height)110     virtual void resize(Uint32 width, Uint32 height) {
111         Sint32 availableWidth = width;
112 
113         int numRemainingWidgets = containedWidgets.size();
114 
115         // Find objects that are not allowed to be resized or have a fixed width
116         // also find the sum of all weights
117         double weightSum = 0.0;
118         for(const HBox_WidgetData& widgetData : containedWidgets) {
119             if(widgetData.pWidget->resizingXAllowed() == false) {
120                 availableWidth = availableWidth - widgetData.pWidget->getSize().x;
121                 numRemainingWidgets--;
122             } else if(widgetData.fixedWidth > 0) {
123                 availableWidth = availableWidth - widgetData.fixedWidth;
124                 numRemainingWidgets--;
125             } else {
126                 weightSum += widgetData.weight;
127             }
128         }
129 
130         // Under the resizeable widgets find all objects that are oversized (minimum size > availableWidth*weight)
131         // also calculate the weight sum of all the resizeable widgets that are not oversized
132         Sint32 neededOversizeWidth = 0;
133         double notOversizedWeightSum = 0.0;
134         for(const HBox_WidgetData& widgetData : containedWidgets) {
135             if(widgetData.pWidget->resizingXAllowed() == true && widgetData.fixedWidth <= 0) {
136                 if((double) widgetData.pWidget->getMinimumSize().x > availableWidth * (widgetData.weight/weightSum)) {
137                     neededOversizeWidth += widgetData.pWidget->getMinimumSize().x;
138                 } else {
139                     notOversizedWeightSum += widgetData.weight;
140                 }
141             }
142         }
143 
144         Sint32 totalAvailableWidth = availableWidth;
145         for(const HBox_WidgetData& widgetData : containedWidgets) {
146             Sint32 widgetHeight;
147             if(widgetData.pWidget->resizingYAllowed() == true) {
148                 widgetHeight = height;
149             } else {
150                 widgetHeight = widgetData.pWidget->getMinimumSize().y;
151             }
152 
153             if(widgetData.pWidget->resizingXAllowed() == true) {
154                 Sint32 widgetWidth = 0;
155 
156                 if(widgetData.fixedWidth <= 0) {
157                     if(numRemainingWidgets <= 1) {
158                         widgetWidth = availableWidth;
159                     } else if((double) widgetData.pWidget->getMinimumSize().x > totalAvailableWidth * (widgetData.weight/weightSum)) {
160                         widgetWidth = widgetData.pWidget->getMinimumSize().x;
161                     } else {
162                         widgetWidth = (Sint32) ((totalAvailableWidth-neededOversizeWidth) * (widgetData.weight/notOversizedWeightSum));
163                     }
164                     availableWidth -= widgetWidth;
165                     numRemainingWidgets--;
166                 } else {
167                     widgetWidth = widgetData.fixedWidth;
168                 }
169 
170                 widgetData.pWidget->resize(widgetWidth,widgetHeight);
171             } else {
172                 widgetData.pWidget->resize(widgetData.pWidget->getSize().x,widgetHeight);
173             }
174         }
175 
176         Container<HBox_WidgetData>::resize(width,height);
177     }
178 
179     /**
180         This static method creates a dynamic HBox object.
181         The idea behind this method is to simply create a new HBox on the fly and
182         add it to a container. If the container gets destroyed also this HBox will be freed.
183         \return The new created HBox (will be automatically destroyed when it's parent widget is destroyed)
184     */
create()185     static HBox* create() {
186         HBox* hbox = new HBox();
187         hbox->pAllocated = true;
188         return hbox;
189     }
190 
191 protected:
192     /**
193         This method must be overwritten by all container classes. It should return
194         the position of the specified widget.
195         \param widgetData   the widget data to get the position from.
196         \return The position of the left upper corner
197     */
getPosition(const HBox_WidgetData & widgetData)198     virtual Point getPosition(const HBox_WidgetData& widgetData) const {
199         Point p(0,0);
200         for(const HBox_WidgetData& tmpWidgetData : containedWidgets) {
201             if(widgetData.pWidget == tmpWidgetData.pWidget) {
202                 p.y = (getSize().y - tmpWidgetData.pWidget->getSize().y)/2;
203                 return p;
204             } else {
205                 p.x = p.x + tmpWidgetData.pWidget->getSize().x;
206             }
207         }
208 
209         //should not happen
210         return Point(0,0);
211     }
212 };
213 
214 #endif // HBOX_H
215 
216