]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/LayoutItem.cxx
canvas::Layout: support for alignment.
[simgear.git] / simgear / canvas / layout / LayoutItem.cxx
1 // Basic element for layouting canvas elements
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 "LayoutItem.hxx"
20 #include <simgear/canvas/Canvas.hxx>
21
22 namespace simgear
23 {
24 namespace canvas
25 {
26
27   //----------------------------------------------------------------------------
28   Margins::Margins(int m):
29     l(m), t(m), r(m), b(m)
30   {
31
32   }
33
34   //----------------------------------------------------------------------------
35   Margins::Margins(int h, int v):
36     l(h), t(v),
37     r(h), b(v)
38   {
39
40   }
41
42   //----------------------------------------------------------------------------
43   Margins::Margins(int l, int t, int r, int b):
44     l(l), t(t), r(r), b(b)
45   {
46
47   }
48
49   //----------------------------------------------------------------------------
50   int Margins::horiz() const
51   {
52     return l + r;
53   }
54
55   //----------------------------------------------------------------------------
56   int Margins::vert() const
57   {
58     return t + b;
59   }
60
61   //----------------------------------------------------------------------------
62   SGVec2i Margins::size() const
63   {
64     return SGVec2i(horiz(), vert());
65   }
66
67   //----------------------------------------------------------------------------
68   bool Margins::isNull() const
69   {
70     return l == 0 && t == 0 && r == 0 && b == 0;
71   }
72
73   //----------------------------------------------------------------------------
74   const SGVec2i LayoutItem::MAX_SIZE( SGLimits<int>::max(),
75                                       SGLimits<int>::max() );
76
77   //----------------------------------------------------------------------------
78   LayoutItem::LayoutItem():
79     _alignment(AlignFill),
80     _flags(VISIBLE),
81     _size_hint(0, 0),
82     _min_size(0, 0),
83     _max_size(MAX_SIZE)
84   {
85     invalidate();
86   }
87
88   //----------------------------------------------------------------------------
89   LayoutItem::~LayoutItem()
90   {
91
92   }
93
94   //----------------------------------------------------------------------------
95   void LayoutItem::setContentsMargins(const Margins& margins)
96   {
97     _margins = margins;
98   }
99
100   //----------------------------------------------------------------------------
101   void LayoutItem::setContentsMargins(int left, int top, int right, int bottom)
102   {
103     _margins.l = left;
104     _margins.t = top;
105     _margins.r = right;
106     _margins.b = bottom;
107   }
108
109   //----------------------------------------------------------------------------
110   void LayoutItem::setContentsMargin(int margin)
111   {
112     setContentsMargins(margin, margin, margin, margin);
113   }
114
115   //----------------------------------------------------------------------------
116   Margins LayoutItem::getContentsMargins() const
117   {
118     return _margins;
119   }
120
121   //----------------------------------------------------------------------------
122   SGRecti LayoutItem::contentsRect() const
123   {
124     return SGRecti(
125       _geometry.x() + _margins.l,
126       _geometry.y() + _margins.t,
127       std::max(0, _geometry.width() - _margins.horiz()),
128       std::max(0, _geometry.height() - _margins.vert())
129     );
130   }
131
132   //----------------------------------------------------------------------------
133   SGVec2i LayoutItem::sizeHint() const
134   {
135     if( _flags & SIZE_HINT_DIRTY )
136     {
137       _size_hint = sizeHintImpl();
138       _flags &= ~SIZE_HINT_DIRTY;
139     }
140
141     return addClipOverflow(_size_hint, _margins.size());
142   }
143
144   //----------------------------------------------------------------------------
145   SGVec2i LayoutItem::minimumSize() const
146   {
147     if( _flags & MINIMUM_SIZE_DIRTY )
148     {
149       _min_size = minimumSizeImpl();
150       _flags &= ~MINIMUM_SIZE_DIRTY;
151     }
152
153     return addClipOverflow(_min_size, _margins.size());
154   }
155
156   //----------------------------------------------------------------------------
157   SGVec2i LayoutItem::maximumSize() const
158   {
159     if( _flags & MAXIMUM_SIZE_DIRTY )
160     {
161       _max_size = maximumSizeImpl();
162       _flags &= ~MAXIMUM_SIZE_DIRTY;
163     }
164
165     return addClipOverflow(_max_size, _margins.size());
166   }
167
168   //----------------------------------------------------------------------------
169   bool LayoutItem::hasHeightForWidth() const
170   {
171     return false;
172   }
173
174   //----------------------------------------------------------------------------
175   int LayoutItem::heightForWidth(int w) const
176   {
177     int h = heightForWidthImpl(w - _margins.horiz());
178     return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
179   }
180
181   //------------------------------------------------------------------------------
182   int LayoutItem::minimumHeightForWidth(int w) const
183   {
184     int h = minimumHeightForWidthImpl(w - _margins.horiz());
185     return h < 0 ? -1 : SGMisc<int>::addClipOverflow(h, _margins.vert());
186   }
187
188   //----------------------------------------------------------------------------
189   void LayoutItem::setAlignment(uint8_t align)
190   {
191     if( align == _alignment )
192       return;
193
194     _alignment = align;
195     invalidateParent();
196   }
197
198   //----------------------------------------------------------------------------
199   uint8_t LayoutItem::alignment() const
200   {
201     return _alignment;
202   }
203
204   //----------------------------------------------------------------------------
205   void LayoutItem::setVisible(bool visible)
206   {
207     if( visible )
208       _flags &= ~EXPLICITLY_HIDDEN;
209     else
210       _flags |= EXPLICITLY_HIDDEN;
211
212     setVisibleInternal(visible);
213   }
214
215   //----------------------------------------------------------------------------
216   bool LayoutItem::isVisible() const
217   {
218     return _flags & VISIBLE;
219   }
220
221   //----------------------------------------------------------------------------
222   bool LayoutItem::isExplicitlyHidden() const
223   {
224     return _flags & EXPLICITLY_HIDDEN;
225   }
226
227   //----------------------------------------------------------------------------
228   void LayoutItem::invalidate()
229   {
230     _flags |= SIZE_INFO_DIRTY | LAYOUT_DIRTY;
231     invalidateParent();
232   }
233
234   //----------------------------------------------------------------------------
235   void LayoutItem::invalidateParent()
236   {
237     LayoutItemRef parent = _parent.lock();
238     if( parent )
239       parent->invalidate();
240   }
241
242   //----------------------------------------------------------------------------
243   void LayoutItem::update()
244   {
245     if( (_flags & LAYOUT_DIRTY) && isVisible() )
246       contentsRectChanged( contentsRect() );
247   }
248
249   //----------------------------------------------------------------------------
250   void LayoutItem::setGeometry(const SGRecti& geom)
251   {
252     SGRecti ar = alignmentRect(geom);
253     if( ar != _geometry )
254     {
255       _geometry = ar;
256       _flags |= LAYOUT_DIRTY;
257     }
258
259     update();
260   }
261
262   //----------------------------------------------------------------------------
263   SGRecti LayoutItem::geometry() const
264   {
265     return _geometry;
266   }
267
268   //----------------------------------------------------------------------------
269   SGRecti LayoutItem::alignmentRect(const SGRecti& geom) const
270   {
271     uint8_t halign = alignment() & AlignHorizontal_Mask,
272             valign = alignment() & AlignVertical_Mask;
273
274     // Size
275     SGVec2i size = sizeHint();
276
277     if( halign == AlignFill )
278       size.x() = maximumSize().x();
279     size.x() = std::min(size.x(), geom.width());
280
281     if( valign == AlignFill )
282       size.y() = maximumSize().y();
283     else if( hasHeightForWidth() )
284       size.y() = heightForWidth(size.x());
285     size.y() = std::min(size.y(), geom.height());
286
287     // Position
288     SGVec2i pos = geom.pos();
289
290     if( halign & AlignRight )
291       pos.x() += geom.width() - size.x();
292     else if( !(halign & AlignLeft) )
293       pos.x() += (geom.width() - size.x()) / 2;
294
295     if( valign & AlignBottom )
296       pos.y() += geom.height() - size.y();
297     else if( !(valign & AlignTop) )
298       pos.y() += (geom.height() - size.y()) / 2;
299
300     return SGRecti(pos, pos + size);
301   }
302
303   //----------------------------------------------------------------------------
304   void LayoutItem::setCanvas(const CanvasWeakPtr& canvas)
305   {
306     _canvas = canvas;
307   }
308
309   //----------------------------------------------------------------------------
310   CanvasPtr LayoutItem::getCanvas() const
311   {
312     return _canvas.lock();
313   }
314
315   //----------------------------------------------------------------------------
316   void LayoutItem::setParent(const LayoutItemWeakRef& parent)
317   {
318     _parent = parent;
319     LayoutItemRef parent_ref = parent.lock();
320
321     if( parent_ref )
322       // Only change the canvas if there is a new parent. If the item is removed
323       // keep the old canvas, as it may be used for example during the call to
324       // onRemove.
325       setCanvas(parent_ref->_canvas);
326
327     setVisibleInternal(!parent_ref || parent_ref->isVisible());
328   }
329
330   //----------------------------------------------------------------------------
331   LayoutItemRef LayoutItem::getParent() const
332   {
333     return _parent.lock();
334   }
335
336   //----------------------------------------------------------------------------
337   SGVec2i LayoutItem::sizeHintImpl() const
338   {
339     return _size_hint;
340   }
341
342   //----------------------------------------------------------------------------
343   SGVec2i LayoutItem::minimumSizeImpl() const
344   {
345     return _min_size;
346   }
347
348   //----------------------------------------------------------------------------
349   SGVec2i LayoutItem::maximumSizeImpl() const
350   {
351     return _max_size;
352   }
353
354   //----------------------------------------------------------------------------
355   int LayoutItem::heightForWidthImpl(int w) const
356   {
357     return -1;
358   }
359
360   //------------------------------------------------------------------------------
361   int LayoutItem::minimumHeightForWidthImpl(int w) const
362   {
363     return heightForWidth(w);
364   }
365
366   //----------------------------------------------------------------------------
367   void LayoutItem::setVisibleInternal(bool visible)
368   {
369     LayoutItemRef parent = getParent();
370     if( isExplicitlyHidden() || (parent && !parent->isVisible()) )
371       visible = false;
372
373     if( isVisible() == visible )
374       return;
375
376     invalidateParent();
377
378     if( visible )
379       _flags |= VISIBLE;
380     else
381       _flags &= ~VISIBLE;
382
383     visibilityChanged(visible);
384   }
385
386   //----------------------------------------------------------------------------
387   void LayoutItem::callSetVisibleInternal(LayoutItem* item, bool visible)
388   {
389     item->setVisibleInternal(visible);
390   }
391
392 } // namespace canvas
393 } // namespace simgear