1 // NasalCanvas.cxx -- expose Canvas classes to Nasal
3 // Written by James Turner, started 2012.
5 // Copyright (C) 2012 James Turner
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "NasalCanvas.hxx"
26 #include <Canvas/canvas_mgr.hxx>
27 #include <Canvas/gui_mgr.hxx>
28 #include <Main/globals.hxx>
29 #include <Scripting/NasalSys.hxx>
31 #include <osgGA/GUIEventAdapter>
33 #include <simgear/sg_inlines.h>
35 #include <simgear/canvas/Canvas.hxx>
36 #include <simgear/canvas/CanvasWindow.hxx>
37 #include <simgear/canvas/elements/CanvasElement.hxx>
38 #include <simgear/canvas/elements/CanvasText.hxx>
39 #include <simgear/canvas/events/CustomEvent.hxx>
40 #include <simgear/canvas/events/MouseEvent.hxx>
42 #include <simgear/nasal/cppbind/from_nasal.hxx>
43 #include <simgear/nasal/cppbind/to_nasal.hxx>
44 #include <simgear/nasal/cppbind/NasalHash.hxx>
45 #include <simgear/nasal/cppbind/Ghost.hxx>
47 extern naRef propNodeGhostCreate(naContext c, SGPropertyNode* n);
49 namespace sc = simgear::canvas;
51 template<class Element>
52 naRef elementGetNode(Element& element, naContext c)
54 return propNodeGhostCreate(c, element.getProps());
57 typedef nasal::Ghost<sc::EventPtr> NasalEvent;
58 typedef nasal::Ghost<sc::CustomEventPtr> NasalCustomEvent;
59 typedef nasal::Ghost<sc::MouseEventPtr> NasalMouseEvent;
61 struct CustomEventDetailWrapper;
62 typedef SGSharedPtr<CustomEventDetailWrapper> CustomEventDetailPtr;
63 typedef nasal::Ghost<CustomEventDetailPtr> NasalCustomEventDetail;
65 typedef nasal::Ghost<simgear::PropertyBasedElementPtr> NasalPropertyBasedElement;
66 typedef nasal::Ghost<sc::CanvasPtr> NasalCanvas;
67 typedef nasal::Ghost<sc::ElementPtr> NasalElement;
68 typedef nasal::Ghost<sc::GroupPtr> NasalGroup;
69 typedef nasal::Ghost<sc::TextPtr> NasalText;
70 typedef nasal::Ghost<sc::WindowWeakPtr> NasalWindow;
72 naRef to_nasal_helper(naContext c, const osg::BoundingBox& bb)
74 std::vector<float> bb_vec(4);
75 bb_vec[0] = bb._min.x();
76 bb_vec[1] = bb._min.y();
77 bb_vec[2] = bb._max.x();
78 bb_vec[3] = bb._max.y();
80 return nasal::to_nasal(c, bb_vec);
83 SGPropertyNode* from_nasal_helper(naContext c, naRef ref, SGPropertyNode**)
85 SGPropertyNode* props = ghostToPropNode(ref);
87 naRuntimeError(c, "Not a SGPropertyNode ghost.");
92 sc::CanvasWeakPtr from_nasal_helper(naContext c, naRef ref, sc::CanvasWeakPtr const*)
94 return nasal::from_nasal<sc::CanvasPtr>(c, ref);
97 CanvasMgr& requireCanvasMgr(naContext c)
99 CanvasMgr* canvas_mgr =
100 static_cast<CanvasMgr*>(globals->get_subsystem("Canvas"));
102 naRuntimeError(c, "Failed to get Canvas subsystem");
107 GUIMgr& requireGUIMgr(naContext c)
110 static_cast<GUIMgr*>(globals->get_subsystem("CanvasGUI"));
112 naRuntimeError(c, "Failed to get CanvasGUI subsystem");
118 * Create new Canvas and get ghost for it.
120 static naRef f_createCanvas(const nasal::CallContext& ctx)
122 return NasalCanvas::create(ctx.c, requireCanvasMgr(ctx.c).createCanvas());
126 * Create new Window and get ghost for it.
128 static naRef f_createWindow(const nasal::CallContext& ctx)
130 return NasalWindow::create
133 requireGUIMgr(ctx.c).createWindow( ctx.getArg<std::string>(0) )
138 * Get ghost for existing Canvas.
140 static naRef f_getCanvas(naContext c, naRef me, int argc, naRef* args)
142 nasal::CallContext ctx(c, argc, args);
143 SGPropertyNode& props = *ctx.requireArg<SGPropertyNode*>(0);
144 CanvasMgr& canvas_mgr = requireCanvasMgr(c);
146 sc::CanvasPtr canvas;
147 if( canvas_mgr.getPropertyRoot() == props.getParent() )
149 // get a canvas specified by its root node
150 canvas = canvas_mgr.getCanvas( props.getIndex() );
151 if( !canvas || canvas->getProps() != &props )
156 // get a canvas by name
157 if( props.hasValue("name") )
158 canvas = canvas_mgr.getCanvas( props.getStringValue("name") );
159 else if( props.hasValue("index") )
160 canvas = canvas_mgr.getCanvas( props.getIntValue("index") );
163 return NasalCanvas::create(c, canvas);
166 naRef f_canvasCreateGroup(sc::Canvas& canvas, const nasal::CallContext& ctx)
168 return NasalGroup::create
171 canvas.createGroup( ctx.getArg<std::string>(0) )
176 * Get group containing all gui windows
178 naRef f_getDesktop(naContext c, naRef me, int argc, naRef* args)
180 return NasalGroup::create(c, requireGUIMgr(c).getDesktop());
183 naRef f_groupCreateChild(sc::Group& group, const nasal::CallContext& ctx)
185 return NasalElement::create
188 group.createChild( ctx.requireArg<std::string>(0),
189 ctx.getArg<std::string>(1) )
193 naRef f_groupGetChild(sc::Group& group, const nasal::CallContext& ctx)
195 return NasalElement::create
198 group.getChild( ctx.requireArg<SGPropertyNode*>(0) )
202 naRef f_groupGetElementById(sc::Group& group, const nasal::CallContext& ctx)
204 return NasalElement::create
207 group.getElementById( ctx.requireArg<std::string>(0) )
211 static void propElementSetData( simgear::PropertyBasedElement& el,
212 const std::string& name,
217 return el.removeDataProp(name);
219 std::string val = nasal::from_nasal<std::string>(c, ref);
223 long val_long = strtol(val.c_str(), &end, 10);
225 return el.setDataProp(name, val_long);
227 double val_double = strtod(val.c_str(), &end);
229 return el.setDataProp(name, val_double);
231 el.setDataProp(name, val);
235 * Accessor for HTML5 data properties.
237 * # set single property:
238 * el.data("myKey", 5);
240 * # set multiple properties
241 * el.data({myProp1: 12, myProp2: "test"});
243 * # get value of properties
244 * el.data("myKey"); # 5
245 * el.data("myProp2"); # "test"
247 * # remove a single property
248 * el.data("myKey", nil);
250 * # remove multiple properties
251 * el.data({myProp1: nil, myProp2: nil});
253 * # set and remove multiple properties
254 * el.data({newProp: "some text...", removeProp: nil});
257 * @see http://api.jquery.com/data/
259 static naRef f_propElementData( simgear::PropertyBasedElement& el,
260 const nasal::CallContext& ctx )
264 // Add/delete properties given as hash
265 nasal::Hash obj = ctx.requireArg<nasal::Hash>(0);
266 for(nasal::Hash::iterator it = obj.begin(); it != obj.end(); ++it)
267 propElementSetData(el, it->getKey(), ctx.c, it->getValue<naRef>());
269 return ctx.to_nasal(&el);
272 std::string name = ctx.getArg<std::string>(0);
277 // name + additional argument -> add/delete property
278 SGPropertyNode* node = el.getDataProp<SGPropertyNode*>(name);
282 return ctx.to_nasal( node->getStringValue() );
286 // only name -> get property
287 propElementSetData(el, name, ctx.c, ctx.requireArg<naRef>(1));
288 return ctx.to_nasal(&el);
296 naRef f_eventGetModifier(sc::MouseEvent& event, naContext)
298 return naNum((event.getModifiers() & Mask) != 0);
301 static naRef f_createCustomEvent(const nasal::CallContext& ctx)
303 std::string const& type = ctx.requireArg<std::string>(0);
307 simgear::StringMap detail;
310 nasal::Hash const& cfg = ctx.requireArg<nasal::Hash>(1);
311 naRef na_detail = cfg.get("detail");
312 if( naIsHash(na_detail) )
313 detail = ctx.from_nasal<simgear::StringMap>(na_detail);
316 return NasalCustomEvent::create(
318 sc::CustomEventPtr(new sc::CustomEvent(type, detail))
322 struct CustomEventDetailWrapper:
325 sc::CustomEventPtr _event;
327 CustomEventDetailWrapper(const sc::CustomEventPtr& event):
333 bool _get( const std::string& key,
334 std::string& value_out ) const
339 simgear::StringMap::const_iterator it = _event->detail.find(key);
340 if( it == _event->detail.end() )
343 value_out = it->second;
347 bool _set( const std::string& key,
348 const std::string& value )
353 _event->detail[ key ] = value;
358 static naRef f_customEventGetDetail( sc::CustomEvent& event,
361 return nasal::to_nasal(
363 CustomEventDetailPtr(new CustomEventDetailWrapper(&event))
367 naRef to_nasal_helper(naContext c, const sc::ElementWeakPtr& el)
369 return NasalElement::create(c, el.lock());
372 naRef to_nasal_helper(naContext c, const sc::CanvasWeakPtr& canvas)
374 return NasalCanvas::create(c, canvas.lock());
377 naRef initNasalCanvas(naRef globals, naContext c)
379 using osgGA::GUIEventAdapter;
380 NasalEvent::init("canvas.Event")
381 .member("type", &sc::Event::getTypeString)
382 .member("target", &sc::Event::getTarget)
383 .member("currentTarget", &sc::Event::getCurrentTarget)
384 .method("stopPropagation", &sc::Event::stopPropagation);
386 NasalCustomEvent::init("canvas.CustomEvent")
388 .member("detail", &f_customEventGetDetail, &sc::CustomEvent::setDetail);
389 NasalCustomEventDetail::init("canvas.CustomEventDetail")
390 ._get(&CustomEventDetailWrapper::_get)
391 ._set(&CustomEventDetailWrapper::_set);
393 NasalMouseEvent::init("canvas.MouseEvent")
395 .member("screenX", &sc::MouseEvent::getScreenX)
396 .member("screenY", &sc::MouseEvent::getScreenY)
397 .member("clientX", &sc::MouseEvent::getClientX)
398 .member("clientY", &sc::MouseEvent::getClientY)
399 .member("localX", &sc::MouseEvent::getLocalX)
400 .member("localY", &sc::MouseEvent::getLocalY)
401 .member("deltaX", &sc::MouseEvent::getDeltaX)
402 .member("deltaY", &sc::MouseEvent::getDeltaY)
403 .member("button", &sc::MouseEvent::getButton)
404 .member("buttons", &sc::MouseEvent::getButtonMask)
405 .member("modifiers", &sc::MouseEvent::getModifiers)
406 .member("ctrlKey", &f_eventGetModifier<GUIEventAdapter::MODKEY_CTRL>)
407 .member("shiftKey", &f_eventGetModifier<GUIEventAdapter::MODKEY_SHIFT>)
408 .member("altKey", &f_eventGetModifier<GUIEventAdapter::MODKEY_ALT>)
409 .member("metaKey", &f_eventGetModifier<GUIEventAdapter::MODKEY_META>)
410 .member("click_count", &sc::MouseEvent::getCurrentClickCount);
412 NasalPropertyBasedElement::init("PropertyBasedElement")
413 .method("data", &f_propElementData);
414 NasalCanvas::init("Canvas")
415 .bases<NasalPropertyBasedElement>()
416 .member("_node_ghost", &elementGetNode<sc::Canvas>)
417 .member("size_x", &sc::Canvas::getSizeX)
418 .member("size_y", &sc::Canvas::getSizeY)
419 .method("_createGroup", &f_canvasCreateGroup)
420 .method("_getGroup", &sc::Canvas::getGroup)
421 .method("addEventListener", &sc::Canvas::addEventListener)
422 .method("dispatchEvent", &sc::Canvas::dispatchEvent);
423 NasalElement::init("canvas.Element")
424 .bases<NasalPropertyBasedElement>()
425 .member("_node_ghost", &elementGetNode<sc::Element>)
426 .method("_getParent", &sc::Element::getParent)
427 .method("_getCanvas", &sc::Element::getCanvas)
428 .method("addEventListener", &sc::Element::addEventListener)
429 .method("dispatchEvent", &sc::Element::dispatchEvent)
430 .method("getBoundingBox", &sc::Element::getBoundingBox)
431 .method("getTightBoundingBox", &sc::Element::getTightBoundingBox);
432 NasalGroup::init("canvas.Group")
433 .bases<NasalElement>()
434 .method("_createChild", &f_groupCreateChild)
435 .method("_getChild", &f_groupGetChild)
436 .method("_getElementById", &f_groupGetElementById);
437 NasalText::init("canvas.Text")
438 .bases<NasalElement>()
439 .method("getNearestCursor", &sc::Text::getNearestCursor);
441 NasalWindow::init("canvas.Window")
442 .bases<NasalElement>()
443 .member("_node_ghost", &elementGetNode<sc::Window>)
444 .method("_getCanvasDecoration", &sc::Window::getCanvasDecoration);
446 nasal::Hash globals_module(globals, c),
447 canvas_module = globals_module.createHash("canvas");
449 canvas_module.set("_newCanvasGhost", f_createCanvas);
450 canvas_module.set("_newWindowGhost", f_createWindow);
451 canvas_module.set("_getCanvasGhost", f_getCanvas);
452 canvas_module.set("_getDesktopGhost", f_getDesktop);
454 canvas_module.createHash("CustomEvent")
455 .set("new", &f_createCustomEvent);