]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/BoxLayout.cxx
canvas::Layout: remove/get child items.
[simgear.git] / simgear / canvas / layout / BoxLayout.cxx
1 // Align items horizontally or vertically in a box
2 //
3 // Copyright (C) 2014  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Library General Public License for more details.
14 //
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
18
19 #include "BoxLayout.hxx"
20 #include "SpacerItem.hxx"
21 #include <simgear/canvas/Canvas.hxx>
22
23 namespace simgear
24 {
25 namespace canvas
26 {
27
28   //----------------------------------------------------------------------------
29   BoxLayout::BoxLayout(Direction dir):
30     _padding(5)
31   {
32     setDirection(dir);
33   }
34
35   //----------------------------------------------------------------------------
36   void BoxLayout::addItem(const LayoutItemRef& item)
37   {
38     return addItem(item, 0);
39   }
40
41   //----------------------------------------------------------------------------
42   void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
43   {
44     insertItem(-1, item, stretch);
45   }
46
47   //----------------------------------------------------------------------------
48   void BoxLayout::addStretch(int stretch)
49   {
50     insertStretch(-1, stretch);
51   }
52
53   //----------------------------------------------------------------------------
54   void BoxLayout::addSpacing(int size)
55   {
56     insertSpacing(-1, size);
57   }
58
59   //----------------------------------------------------------------------------
60   void BoxLayout::insertItem(int index, const LayoutItemRef& item, int stretch)
61   {
62     ItemData item_data = {0};
63     item_data.layout_item = item;
64     item_data.stretch = std::max(0, stretch);
65
66     item->setCanvas(_canvas);
67     item->setParent(this);
68
69     if( index < 0 )
70       _layout_items.push_back(item_data);
71     else
72       _layout_items.insert(_layout_items.begin() + index, item_data);
73
74     invalidate();
75   }
76
77   //----------------------------------------------------------------------------
78   void BoxLayout::insertStretch(int index, int stretch)
79   {
80     insertItem(index, LayoutItemRef(new SpacerItem()), stretch);
81   }
82
83   //----------------------------------------------------------------------------
84   void BoxLayout::insertSpacing(int index, int size)
85   {
86     SGVec2i size_hint = horiz()
87                           ? SGVec2i(size, 0)
88                           : SGVec2i(0, size),
89                 max_size  = size_hint;
90
91     insertItem(index, LayoutItemRef(new SpacerItem(size_hint, max_size)));
92   }
93
94   //----------------------------------------------------------------------------
95   size_t BoxLayout::count() const
96   {
97     return _layout_items.size();
98   }
99
100   //----------------------------------------------------------------------------
101   LayoutItemRef BoxLayout::itemAt(size_t index)
102   {
103     if( index >= _layout_items.size() )
104       return LayoutItemRef();
105
106     return _layout_items[index].layout_item;
107   }
108
109   //----------------------------------------------------------------------------
110   LayoutItemRef BoxLayout::takeAt(size_t index)
111   {
112     if( index >= _layout_items.size() )
113       return LayoutItemRef();
114
115     LayoutItems::iterator it = _layout_items.begin() + index;
116     LayoutItemRef item = it->layout_item;
117     _layout_items.erase(it);
118
119     invalidate();
120
121     return item;
122   }
123
124   //----------------------------------------------------------------------------
125   void BoxLayout::setStretch(size_t index, int stretch)
126   {
127     if( index >= _layout_items.size() )
128       return;
129
130     _layout_items.at(index).stretch = std::max(0, stretch);
131     invalidate();
132   }
133
134   //----------------------------------------------------------------------------
135   int BoxLayout::stretch(size_t index) const
136   {
137     if( index >= _layout_items.size() )
138       return 0;
139
140     return _layout_items.at(index).stretch;
141   }
142
143   //----------------------------------------------------------------------------
144   void BoxLayout::setSpacing(int spacing)
145   {
146     if( spacing == _padding )
147       return;
148
149     _padding = spacing;
150     invalidate();
151   }
152
153   //----------------------------------------------------------------------------
154   int BoxLayout::spacing() const
155   {
156     return _padding;
157   }
158
159   //----------------------------------------------------------------------------
160   void BoxLayout::setDirection(Direction dir)
161   {
162     _direction = dir;
163     _get_layout_coord = &SGVec2i::x;
164     _get_fixed_coord = &SGVec2i::y;
165
166     if( !horiz() )
167       std::swap(_get_layout_coord, _get_fixed_coord);
168
169     invalidate();
170   }
171
172   //----------------------------------------------------------------------------
173   BoxLayout::Direction BoxLayout::direction() const
174   {
175     return _direction;
176   }
177
178   //----------------------------------------------------------------------------
179   void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
180   {
181     _canvas = canvas;
182
183     for(size_t i = 0; i < _layout_items.size(); ++i)
184       _layout_items[i].layout_item->setCanvas(canvas);
185   }
186
187   //----------------------------------------------------------------------------
188   bool BoxLayout::horiz() const
189   {
190     return (_direction == LeftToRight) || (_direction == RightToLeft);
191   }
192
193   //----------------------------------------------------------------------------
194   void BoxLayout::updateSizeHints() const
195   {
196     SGVec2i min_size(0, 0),
197             max_size(0, 0),
198             size_hint(0, 0);
199
200     _layout_data.reset();
201     bool is_first = true;
202
203     for(size_t i = 0; i < _layout_items.size(); ++i)
204     {
205       // TODO check visible
206
207       ItemData& item_data = _layout_items[i];
208       LayoutItem const& item = *item_data.layout_item;
209
210       item_data.min_size  = (item.minimumSize().*_get_layout_coord)();
211       item_data.max_size  = (item.maximumSize().*_get_layout_coord)();
212       item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
213
214       if( !dynamic_cast<SpacerItem*>(item_data.layout_item.get()) )
215       {
216         if( is_first )
217         {
218           item_data.padding_orig = 0;
219           is_first = false;
220         }
221         else
222         {
223           item_data.padding_orig = _padding;
224           _layout_data.padding += item_data.padding_orig;
225         }
226       }
227
228       // Add sizes of all children in layout direction
229       safeAdd(min_size.x(),  item_data.min_size);
230       safeAdd(max_size.x(),  item_data.max_size);
231       safeAdd(size_hint.x(), item_data.size_hint);
232
233       // Take maximum in fixed (non-layouted) direction
234       min_size.y()  = std::max( min_size.y(),
235                                 (item.minimumSize().*_get_fixed_coord)() );
236       max_size.y()  = std::max( max_size.y(),
237                                 (item.maximumSize().*_get_fixed_coord)() );
238       size_hint.y() = std::max( size_hint.y(),
239                                 (item.sizeHint().*_get_fixed_coord)() );
240     }
241
242     safeAdd(min_size.x(),  _layout_data.padding);
243     safeAdd(max_size.x(),  _layout_data.padding);
244     safeAdd(size_hint.x(), _layout_data.padding);
245
246     _layout_data.min_size = min_size.x();
247     _layout_data.max_size = max_size.x();
248     _layout_data.size_hint = size_hint.x();
249
250     _min_size.x()  = (min_size.*_get_layout_coord)();
251     _max_size.x()  = (max_size.*_get_layout_coord)();
252     _size_hint.x() = (size_hint.*_get_layout_coord)();
253
254     _min_size.y()  = (min_size.*_get_fixed_coord)();
255     _max_size.y()  = (max_size.*_get_fixed_coord)();
256     _size_hint.y() = (size_hint.*_get_fixed_coord)();
257
258     _flags &= ~SIZE_INFO_DIRTY;
259   }
260
261   //----------------------------------------------------------------------------
262   SGVec2i BoxLayout::sizeHintImpl() const
263   {
264     updateSizeHints();
265     return _size_hint;
266   }
267
268   //----------------------------------------------------------------------------
269   SGVec2i BoxLayout::minimumSizeImpl() const
270   {
271     updateSizeHints();
272     return _min_size;
273   }
274
275   //----------------------------------------------------------------------------
276   SGVec2i BoxLayout::maximumSizeImpl() const
277   {
278     updateSizeHints();
279     return _max_size;
280   }
281
282   //----------------------------------------------------------------------------
283   void BoxLayout::doLayout(const SGRecti& geom)
284   {
285     if( _flags & SIZE_INFO_DIRTY )
286       updateSizeHints();
287
288     _layout_data.size = (geom.size().*_get_layout_coord)();
289     distribute(_layout_items, _layout_data);
290
291     int fixed_size = (geom.size().*_get_fixed_coord)();
292     SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
293                      (geom.pos().*_get_fixed_coord)() );
294
295     bool reverse = (_direction == RightToLeft) || (_direction == BottomToTop);
296     if( reverse )
297       cur_pos.x() += (geom.size().*_get_layout_coord)();
298
299     // TODO handle reverse layouting (rtl/btt)
300     for(size_t i = 0; i < _layout_items.size(); ++i)
301     {
302       ItemData const& data = _layout_items[i];
303       cur_pos.x() += reverse ? -data.padding - data.size : data.padding;
304
305       SGVec2i size(
306         data.size,
307         std::min( (data.layout_item->maximumSize().*_get_fixed_coord)(),
308                   fixed_size )
309       );
310
311       // Center in fixed direction (TODO allow specifying alignment)
312       int offset_fixed = (fixed_size - size.y()) / 2;
313       cur_pos.y() += offset_fixed;
314
315       data.layout_item->setGeometry(SGRecti(
316         (cur_pos.*_get_layout_coord)(),
317         (cur_pos.*_get_fixed_coord)(),
318         (size.*_get_layout_coord)(),
319         (size.*_get_fixed_coord)()
320       ));
321
322       if( !reverse )
323         cur_pos.x() += data.size;
324       cur_pos.y() -= offset_fixed;
325     }
326   }
327
328   //----------------------------------------------------------------------------
329   HBoxLayout::HBoxLayout():
330     BoxLayout(LeftToRight)
331   {
332
333   }
334
335   //----------------------------------------------------------------------------
336   VBoxLayout::VBoxLayout():
337     BoxLayout(TopToBottom)
338   {
339
340   }
341
342 } // namespace canvas
343 } // namespace simgear