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