]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/BoxLayout.cxx
Canvas/Layout: tweak the way elements are exposed to Nasal.
[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 <simgear/canvas/Canvas.hxx>
21
22 namespace simgear
23 {
24 namespace canvas
25 {
26
27   //----------------------------------------------------------------------------
28   BoxLayout::BoxLayout(Direction dir):
29     _padding(5)
30   {
31     setDirection(dir);
32   }
33
34   //----------------------------------------------------------------------------
35   void BoxLayout::addItem(const LayoutItemRef& item)
36   {
37     return addItem(item, 0);
38   }
39
40   //----------------------------------------------------------------------------
41   void BoxLayout::addItem(const LayoutItemRef& item, int stretch)
42   {
43     ItemData item_data = {0};
44     item_data.layout_item = item;
45     item_data.stretch = std::max(0, stretch);
46
47     item->setCanvas(_canvas);
48     item->setParent(this);
49
50     _layout_items.push_back(item_data);
51
52     invalidate();
53   }
54
55   //----------------------------------------------------------------------------
56   void BoxLayout::setStretch(size_t index, int stretch)
57   {
58     if( index >= _layout_items.size() )
59       return;
60
61     _layout_items.at(index).stretch = std::max(0, stretch);
62     invalidate();
63   }
64
65   //----------------------------------------------------------------------------
66   int BoxLayout::stretch(size_t index) const
67   {
68     if( index >= _layout_items.size() )
69       return 0;
70
71     return _layout_items.at(index).stretch;
72   }
73
74   //----------------------------------------------------------------------------
75   void BoxLayout::setSpacing(int spacing)
76   {
77     if( spacing == _padding )
78       return;
79
80     _padding = spacing;
81     invalidate();
82   }
83
84   //----------------------------------------------------------------------------
85   int BoxLayout::spacing() const
86   {
87     return _padding;
88   }
89
90   //----------------------------------------------------------------------------
91   void BoxLayout::setDirection(Direction dir)
92   {
93     _get_layout_coord = &SGVec2i::x;
94     _get_fixed_coord = &SGVec2i::y;
95
96     if( dir == TopToBottom || dir == BottomToTop )
97       std::swap(_get_layout_coord, _get_fixed_coord);
98
99     _reverse = (dir == RightToLeft || dir == BottomToTop );
100
101     invalidate();
102   }
103
104   //----------------------------------------------------------------------------
105   BoxLayout::Direction BoxLayout::direction() const
106   {
107     if( _get_layout_coord == static_cast<CoordGetter>(&SGVec2i::x) )
108       return _reverse ? RightToLeft : LeftToRight;
109     else
110       return _reverse ? BottomToTop : TopToBottom;
111   }
112
113   //----------------------------------------------------------------------------
114   void BoxLayout::setCanvas(const CanvasWeakPtr& canvas)
115   {
116     _canvas = canvas;
117
118     for(size_t i = 0; i < _layout_items.size(); ++i)
119       _layout_items[i].layout_item->setCanvas(canvas);
120   }
121
122   //----------------------------------------------------------------------------
123   void BoxLayout::updateSizeHints() const
124   {
125     SGVec2i min_size(0, 0),
126             max_size(0, 0),
127             size_hint(0, 0);
128
129     _layout_data.reset();
130     bool is_first = true;
131
132     for(size_t i = 0; i < _layout_items.size(); ++i)
133     {
134       // TODO check visible
135
136       ItemData& item_data = _layout_items[i];
137       LayoutItem const& item = *item_data.layout_item;
138
139       item_data.min_size  = (item.minimumSize().*_get_layout_coord)();
140       item_data.max_size  = (item.maximumSize().*_get_layout_coord)();
141       item_data.size_hint = (item.sizeHint().*_get_layout_coord)();
142
143       if( is_first )
144       {
145         item_data.padding_orig = 0;
146         is_first = false;
147       }
148       else
149         item_data.padding_orig = _padding;
150
151      _layout_data.padding += item_data.padding_orig;
152
153       // Add sizes of all children in layout direction
154       safeAdd(min_size.x(),  item_data.min_size);
155       safeAdd(max_size.x(),  item_data.max_size);
156       safeAdd(size_hint.x(), item_data.size_hint);
157
158       // Take maximum in fixed (non-layouted) direction
159       min_size.y()  = std::max( min_size.y(),
160                                 (item.minimumSize().*_get_fixed_coord)() );
161       max_size.y()  = std::max( max_size.y(),
162                                 (item.maximumSize().*_get_fixed_coord)() );
163       size_hint.y() = std::max( min_size.y(),
164                                 (item.sizeHint().*_get_fixed_coord)() );
165     }
166
167     safeAdd(min_size.x(),  _layout_data.padding);
168     safeAdd(max_size.x(),  _layout_data.padding);
169     safeAdd(size_hint.x(), _layout_data.padding);
170
171     _layout_data.min_size = min_size.x();
172     _layout_data.max_size = max_size.x();
173     _layout_data.size_hint = size_hint.x();
174
175     _min_size.x()  = (min_size.*_get_layout_coord)();
176     _max_size.x()  = (max_size.*_get_layout_coord)();
177     _size_hint.x() = (size_hint.*_get_layout_coord)();
178
179     _min_size.y()  = (min_size.*_get_fixed_coord)();
180     _max_size.y()  = (max_size.*_get_fixed_coord)();
181     _size_hint.y() = (size_hint.*_get_fixed_coord)();
182
183     _flags &= ~SIZE_INFO_DIRTY;
184   }
185
186   //----------------------------------------------------------------------------
187   SGVec2i BoxLayout::sizeHintImpl() const
188   {
189     updateSizeHints();
190     return _size_hint;
191   }
192
193   //----------------------------------------------------------------------------
194   SGVec2i BoxLayout::minimumSizeImpl() const
195   {
196     updateSizeHints();
197     return _min_size;
198   }
199
200   //----------------------------------------------------------------------------
201   SGVec2i BoxLayout::maximumSizeImpl() const
202   {
203     updateSizeHints();
204     return _max_size;
205   }
206
207   //----------------------------------------------------------------------------
208   void BoxLayout::doLayout(const SGRecti& geom)
209   {
210     if( _flags & SIZE_INFO_DIRTY )
211       updateSizeHints();
212
213     _layout_data.size = (geom.size().*_get_layout_coord)();
214     distribute(_layout_items, _layout_data);
215
216     int fixed_size = (geom.size().*_get_fixed_coord)();
217     SGVec2i cur_pos( (geom.pos().*_get_layout_coord)(),
218                      (geom.pos().*_get_fixed_coord)() );
219     if( _reverse )
220       cur_pos.x() += (geom.size().*_get_layout_coord)();
221
222     // TODO handle reverse layouting (rtl/btt)
223     for(size_t i = 0; i < _layout_items.size(); ++i)
224     {
225       ItemData const& data = _layout_items[i];
226       cur_pos.x() += _reverse ? -data.padding - data.size : data.padding;
227
228       SGVec2i size(
229         data.size,
230         std::min( (data.layout_item->maximumSize().*_get_fixed_coord)(),
231                   fixed_size )
232       );
233
234       // Center in fixed direction (TODO allow specifying alignment)
235       int offset_fixed = (fixed_size - size.y()) / 2;
236       cur_pos.y() += offset_fixed;
237
238       data.layout_item->setGeometry(SGRecti(
239         (cur_pos.*_get_layout_coord)(),
240         (cur_pos.*_get_fixed_coord)(),
241         (size.*_get_layout_coord)(),
242         (size.*_get_fixed_coord)()
243       ));
244
245       if( !_reverse )
246         cur_pos.x() += data.size;
247       cur_pos.y() -= offset_fixed;
248     }
249   }
250
251   //----------------------------------------------------------------------------
252   HBoxLayout::HBoxLayout():
253     BoxLayout(LeftToRight)
254   {
255
256   }
257
258   //----------------------------------------------------------------------------
259   VBoxLayout::VBoxLayout():
260     BoxLayout(TopToBottom)
261   {
262
263   }
264
265 } // namespace canvas
266 } // namespace simgear