]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/layout/NasalWidget.cxx
canvas::Layout: keep user provided size hints.
[simgear.git] / simgear / canvas / layout / NasalWidget.cxx
1 // Glue for GUI layout items implemented in Nasal space
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 "NasalWidget.hxx"
20
21 #include <simgear/canvas/Canvas.hxx>
22 #include <simgear/nasal/cppbind/Ghost.hxx>
23
24 namespace simgear
25 {
26 namespace canvas
27 {
28
29   //----------------------------------------------------------------------------
30   NasalWidget::NasalWidget(naRef impl):
31     Object(impl),
32     _layout_size_hint(32, 32),
33     _layout_min_size(16, 16),
34     _layout_max_size(MAX_SIZE),
35     _user_size_hint(0, 0),
36     _user_min_size(0, 0),
37     _user_max_size(MAX_SIZE)
38   {
39
40   }
41
42   //----------------------------------------------------------------------------
43   NasalWidget::~NasalWidget()
44   {
45     onRemove();
46   }
47
48   //----------------------------------------------------------------------------
49   void NasalWidget::invalidate()
50   {
51     LayoutItem::invalidate();
52     _flags |= LAYOUT_DIRTY;
53   }
54
55   //----------------------------------------------------------------------------
56   void NasalWidget::setGeometry(const SGRect<int>& geom)
57   {
58     if( _geometry != geom )
59       _geometry = geom;
60     else if( !(_flags & LAYOUT_DIRTY) || !_set_geometry )
61       return;
62
63     naContext c = naNewContext();
64     try
65     {
66       _set_geometry(nasal::to_nasal(c, this), geom);
67       _flags &= ~LAYOUT_DIRTY;
68     }
69     catch( std::exception const& ex )
70     {
71       SG_LOG(
72         SG_GUI,
73         SG_WARN,
74         "NasalWidget::setGeometry: callback error: '" << ex.what() << "'"
75       );
76     }
77     naFreeContext(c);
78   }
79
80   //----------------------------------------------------------------------------
81   void NasalWidget::onRemove()
82   {
83     if( !_nasal_impl.valid() )
84       return;
85
86     typedef boost::function<void(nasal::Me)> Deleter;
87
88     naContext c = naNewContext();
89     try
90     {
91       Deleter del =
92         nasal::get_member<Deleter>(c, _nasal_impl.get_naRef(), "onRemove");
93       if( del )
94         del(nasal::to_nasal(c, this));
95     }
96     catch( std::exception const& ex )
97     {
98       SG_LOG(
99         SG_GUI,
100         SG_WARN,
101         "NasalWidget::onRemove: callback error: '" << ex.what() << "'"
102       );
103     }
104     naFreeContext(c);
105   }
106
107   //----------------------------------------------------------------------------
108   void NasalWidget::setSetGeometryFunc(const SetGeometryFunc& func)
109   {
110     _set_geometry = func;
111   }
112
113   //----------------------------------------------------------------------------
114   void NasalWidget::setHeightForWidthFunc(const HeightForWidthFunc& func)
115   {
116     _height_for_width = func;
117     invalidate();
118   }
119
120   //----------------------------------------------------------------------------
121   void NasalWidget::setMinimumHeightForWidthFunc(const HeightForWidthFunc& func)
122   {
123     _min_height_for_width = func;
124     invalidate();
125   }
126
127   //----------------------------------------------------------------------------
128   void NasalWidget::setSizeHint(const SGVec2i& s)
129   {
130     if( _user_size_hint == s )
131       return;
132
133     _user_size_hint = s;
134
135     // TODO just invalidate size_hint? Probably not a performance issue...
136     invalidate();
137   }
138
139   //----------------------------------------------------------------------------
140   void NasalWidget::setMinimumSize(const SGVec2i& s)
141   {
142     if( _user_min_size == s )
143       return;
144
145     _user_min_size = s;
146     invalidate();
147   }
148
149   //----------------------------------------------------------------------------
150   void NasalWidget::setMaximumSize(const SGVec2i& s)
151   {
152     if( _user_max_size == s )
153       return;
154
155     _user_max_size = s;
156     invalidate();
157   }
158
159   //----------------------------------------------------------------------------
160   void NasalWidget::setLayoutSizeHint(const SGVec2i& s)
161   {
162     if( _layout_size_hint == s )
163       return;
164
165     _layout_size_hint = s;
166     invalidate();
167   }
168
169   //----------------------------------------------------------------------------
170   void NasalWidget::setLayoutMinimumSize(const SGVec2i& s)
171   {
172     if( _layout_min_size == s )
173       return;
174
175     _layout_min_size = s;
176     invalidate();
177   }
178
179   //----------------------------------------------------------------------------
180   void NasalWidget::setLayoutMaximumSize(const SGVec2i& s)
181   {
182     if( _layout_max_size == s )
183       return;
184
185     _layout_max_size = s;
186     invalidate();
187   }
188
189   //----------------------------------------------------------------------------
190   bool NasalWidget::hasHeightForWidth() const
191   {
192     return !_height_for_width.empty() || !_min_height_for_width.empty();
193   }
194
195   //----------------------------------------------------------------------------
196   int NasalWidget::heightForWidth(int w) const
197   {
198     return callHeightForWidthFunc( _height_for_width.empty()
199                                  ? _min_height_for_width
200                                  : _height_for_width, w );
201   }
202
203   //----------------------------------------------------------------------------
204   int NasalWidget::minimumHeightForWidth(int w) const
205   {
206     return callHeightForWidthFunc( _min_height_for_width.empty()
207                                  ? _height_for_width
208                                  : _min_height_for_width, w );
209   }
210
211   //----------------------------------------------------------------------------
212   static naRef f_makeNasalWidget(const nasal::CallContext& ctx)
213   {
214     return ctx.to_nasal(NasalWidgetRef(
215       new NasalWidget( ctx.requireArg<naRef>(0) )
216     ));
217   }
218
219   //----------------------------------------------------------------------------
220   void NasalWidget::setupGhost(nasal::Hash& ns)
221   {
222     nasal::Ghost<NasalWidgetRef>::init("canvas.Widget")
223       .bases<LayoutItemRef>()
224       .bases<nasal::ObjectRef>()
225       .method("setSetGeometryFunc", &NasalWidget::setSetGeometryFunc)
226       .method("setMinimumHeightForWidthFunc",
227                                     &NasalWidget::setMinimumHeightForWidthFunc)
228       .method("setHeightForWidthFunc", &NasalWidget::setHeightForWidthFunc)
229       .method("setSizeHint", &NasalWidget::setSizeHint)
230       .method("setMinimumSize", &NasalWidget::setMinimumSize)
231       .method("setMaximumSize", &NasalWidget::setMaximumSize)
232       .method("setLayoutSizeHint", &NasalWidget::setLayoutSizeHint)
233       .method("setLayoutMinimumSize", &NasalWidget::setLayoutMinimumSize)
234       .method("setLayoutMaximumSize", &NasalWidget::setLayoutMaximumSize);
235
236     nasal::Hash widget_hash = ns.createHash("Widget");
237     widget_hash.set("new", &f_makeNasalWidget);
238   }
239
240   //----------------------------------------------------------------------------
241   int NasalWidget::callHeightForWidthFunc( const HeightForWidthFunc& hfw,
242                                            int w ) const
243   {
244     if( hfw.empty() )
245       return -1;
246
247     naContext c = naNewContext();
248     try
249     {
250       return hfw(nasal::to_nasal(c, const_cast<NasalWidget*>(this)), w);
251     }
252     catch( std::exception const& ex )
253     {
254       SG_LOG(
255         SG_GUI,
256         SG_WARN,
257         "NasalWidget.heightForWidth: callback error: '" << ex.what() << "'"
258       );
259     }
260     naFreeContext(c);
261
262     return -1;
263   }
264
265   //----------------------------------------------------------------------------
266   SGVec2i NasalWidget::sizeHintImpl() const
267   {
268     return SGVec2i(
269       _user_size_hint.x() > 0 ? _user_size_hint.x() : _layout_size_hint.x(),
270       _user_size_hint.y() > 0 ? _user_size_hint.y() : _layout_size_hint.y()
271     );
272   }
273
274   //----------------------------------------------------------------------------
275   SGVec2i NasalWidget::minimumSizeImpl() const
276   {
277     return SGVec2i(
278       _user_min_size.x() > 0 ? _user_min_size.x() : _layout_min_size.x(),
279       _user_min_size.y() > 0 ? _user_min_size.y() : _layout_min_size.y()
280     );
281   }
282
283   //----------------------------------------------------------------------------
284   SGVec2i NasalWidget::maximumSizeImpl() const
285   {
286     return SGVec2i(
287       _user_max_size.x() < MAX_SIZE.x() ? _user_max_size.x()
288                                         : _layout_max_size.x(),
289       _user_max_size.y() < MAX_SIZE.y() ? _user_max_size.y()
290                                         : _layout_max_size.y()
291     );
292   }
293
294 } // namespace canvas
295 } // namespace simgear