]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/Layout.cxx
Canvas/Layout: tweak the way elements are exposed to Nasal.
[simgear.git] / simgear / canvas / layout / Layout.cxx
1 // Basic class for canvas layouts
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 "Layout.hxx"
20 #include <simgear/debug/logstream.hxx>
21
22 namespace simgear
23 {
24 namespace canvas
25 {
26
27   //----------------------------------------------------------------------------
28   void Layout::update()
29   {
30     if( !(_flags & (LAYOUT_DIRTY | SIZE_INFO_DIRTY)) )
31       return;
32
33     doLayout(_geometry);
34
35     _flags &= ~LAYOUT_DIRTY;
36   }
37
38   //----------------------------------------------------------------------------
39   void Layout::invalidate()
40   {
41     LayoutItem::invalidate();
42     _flags |= LAYOUT_DIRTY;
43   }
44
45   //----------------------------------------------------------------------------
46   void Layout::setGeometry(const SGRecti& geom)
47   {
48     if( geom == _geometry )
49       return;
50
51     _geometry = geom;
52     _flags |= LAYOUT_DIRTY;
53
54     update();
55   }
56
57   //----------------------------------------------------------------------------
58   void Layout::ItemData::reset()
59   {
60     layout_item = 0;
61     size_hint   = 0;
62     min_size    = 0;
63     max_size    = 0;
64     padding_orig= 0;
65     padding     = 0;
66     size        = 0;
67     stretch     = 0;
68     done        = false;
69   }
70
71   //----------------------------------------------------------------------------
72   void Layout::safeAdd(int& a, int b)
73   {
74     if( SGLimits<int>::max() - b < a )
75       a = SGLimits<int>::max();
76     else
77       a += b;
78   }
79
80   //----------------------------------------------------------------------------
81   void Layout::distribute(std::vector<ItemData>& items, const ItemData& space)
82   {
83     const int num_children = static_cast<int>(items.size());
84     _num_not_done = num_children;
85
86     SG_LOG( SG_GUI,
87             SG_DEBUG,
88             "Layout::distribute(" << num_children << " items)" );
89
90     if( space.size < space.min_size )
91     {
92       // TODO
93       SG_LOG( SG_GUI, SG_WARN, "Layout: not enough size (not implemented)");
94     }
95     else if( space.size < space.max_size )
96     {
97       _sum_stretch = 0;
98       _space_stretch = 0;
99
100       bool less_then_hint = space.size < space.size_hint;
101
102       // Give min_size/size_hint to all items
103       _space_left = space.size
104                   - (less_then_hint ? space.min_size : space.size_hint);
105       for(int i = 0; i < num_children; ++i)
106       {
107         ItemData& d = items[i];
108         d.size = less_then_hint ? d.min_size : d.size_hint;
109         d.padding = d.padding_orig;
110         d.done = d.size >= (less_then_hint ? d.size_hint : d.max_size);
111
112         if( d.done )
113         {
114           _num_not_done -= 1;
115           continue;
116         }
117
118         if( d.stretch > 0 )
119         {
120           _sum_stretch += d.stretch;
121           _space_stretch += d.size;
122         }
123       }
124
125       // Distribute remaining space to increase the size of each item up to its
126       // size_hint/max_size
127       while( _space_left > 0 )
128       {
129         if( _num_not_done <= 0 )
130         {
131           SG_LOG(SG_GUI, SG_WARN, "space left, but no more items?");
132           break;
133         }
134
135         int space_per_element = std::max(1, _space_left / _num_not_done);
136
137         SG_LOG(SG_GUI, SG_DEBUG, "space/element=" << space_per_element);
138
139         for(int i = 0; i < num_children; ++i)
140         {
141           ItemData& d = items[i];
142
143           SG_LOG(
144             SG_GUI,
145             SG_DEBUG,
146             i << ") left=" << _space_left
147               << ", not_done=" << _num_not_done
148               << ", sum=" << _sum_stretch
149               << ", stretch=" << _space_stretch
150               << ", stretch/unit=" << _space_stretch / std::max(1, _sum_stretch)
151           );
152
153           if( d.done )
154             continue;
155
156           if( _sum_stretch > 0 && d.stretch <= 0 )
157             d.done = true;
158           else
159           {
160             int target_size = 0;
161             int max_size = less_then_hint ? d.size_hint : d.max_size;
162
163             if( _sum_stretch > 0 )
164             {
165               target_size = (d.stretch * (_space_left + _space_stretch))
166                           / _sum_stretch;
167
168               // Item would be smaller than minimum size or larger than maximum
169               // size, so just keep bounded size and ignore stretch factor
170               if( target_size <= d.size || target_size >= max_size )
171               {
172                 d.done = true;
173                 _sum_stretch -= d.stretch;
174                 _space_stretch -= d.size;
175
176                 if( target_size >= max_size )
177                   target_size = max_size;
178                 else
179                   target_size = d.size;
180               }
181               else
182                 _space_stretch += target_size - d.size;
183             }
184             else
185             {
186               // Give space evenly to all remaining elements in this round
187               target_size = d.size + std::min(_space_left, space_per_element);
188
189               if( target_size >= max_size )
190               {
191                 d.done = true;
192                 target_size = max_size;
193               }
194             }
195
196             int old_size = d.size;
197             d.size = target_size;
198             _space_left -= d.size - old_size;
199           }
200
201           if( d.done )
202           {
203             _num_not_done -= 1;
204
205             if( _sum_stretch <= 0 && d.stretch > 0 )
206               // Distribute remaining space evenly to all non-stretchable items
207               // in a new round
208               break;
209           }
210         }
211       }
212     }
213     else
214     {
215       _space_left = space.size - space.max_size;
216       for(int i = 0; i < num_children; ++i)
217       {
218         ItemData& d = items[i];
219         d.size = d.max_size;
220
221         // Add superfluous space as padding
222         d.padding = d.padding_orig + _space_left
223                                    // Padding after last child...
224                                    / (_num_not_done + 1);
225
226         _space_left -= d.padding - d.padding_orig;
227         _num_not_done -= 1;
228       }
229     }
230
231     SG_LOG(SG_GUI, SG_DEBUG, "distribute:");
232     for(int i = 0; i < num_children; ++i)
233     {
234       ItemData const& d = items[i];
235       SG_LOG( SG_GUI,
236               SG_DEBUG,
237               i << ") pad=" << d.padding
238                 << ", size = " << d.size );
239     }
240   }
241
242 } // namespace canvas
243 } // namespace simgear