I've today added Nasal support for XML dialog files. Each dialog can have a <nasal> block with <open> and/or <close> element. Nasal code therein is executed on dialog opening and closing. This code and all Nasal code in the dialog bindings run in the same namespace, so one can initialize variables and functions in the <open> block and access them from everywhere in the file. The <close> block is for cleaning up (removing listeners, canceling timers, removing properties etc.)
This allows to write more sophisticated dialogs, and makes external dialog helper *.nas files unnecessary (e.g. the autopilot *.nas file). There's currently one dialog using this feature: location-in-air.xml, which handles the <radio>buttons with embedded Nasal. Attached is another example: stopwatch.xml. It has almost all Nasal in the <open> part, and only a few simple callers in the bindings. To try it out copy the file to $FG_ROOT/gui/dialogs/, and put such an entry in the $FG_ROOT/gui/menubar.xml file: <item> <label>Stopwatch</label> <binding> <command>dialog-show</command> <dialog-name>stopwatch-dialog</dialog-name> </binding> </item> Here it is in action: http://members.aon.at/mfranz/stopwatch.jpg [15 kB] m.
<?xml version="1.0"?> <PropertyList> <name>stopwatch-dialog</name> <layout>vbox</layout> <default-padding>8</default-padding> <nasal> <open> mod = func(x, y) { x - int(x / y) * y } p = "/sim/gui/dialogs/stopwatch-dialog/"; display = props.globals.getNode(p ~ "display", 1); time = props.globals.getNode("/sim/time/elapsed-sec"); accu = nil; start_time = nil; running = 0; start = func { if (!running) { start_time = time.getValue(); running = 1; loop(); } } stop = func { if (running) { running = 0; show(accu += time.getValue() - start_time); } } reset = func { accu = 0; if (running) { start_time = time.getValue(); } else { show(0); } } loop = func { if (running) { show(time.getValue() - start_time + accu); settimer(loop, 0.001); } } show = func(s) { var hours = s / 3600; var minutes = int(mod(s / 60, 60)); var seconds = int(mod(s, 60)); var msec = int(mod(s * 1000, 1000)); var d = sprintf("%3d:%02d:%02d.%03d", hours, minutes, seconds, msec); display.setValue(d); } reset(); </open> <close> running = 0; </close> </nasal> <text> <label>000:00:00.000</label> <halign>center</halign> <live>true</live> <property>/sim/gui/dialogs/stopwatch-dialog/display</property> <font> <name>TIMES_24</name> </font> <color> <red>0</red> <green>1</green> <blue>0</blue> <alpha>1</alpha> </color> </text> <group> <layout>hbox</layout> <default-padding>2</default-padding> <button> <legend>Start</legend> <equal>true</equal> <key>Space</key> <pref-width>47</pref-width> <binding> <command>nasal</command> <script>start()</script> </binding> </button> <button> <legend>Stop</legend> <default>true</default> <pref-width>47</pref-width> <binding> <command>nasal</command> <script>stop()</script> </binding> </button> <button> <legend>Reset</legend> <key>Delete</key> <pref-width>47</pref-width> <binding> <command>nasal</command> <script>reset()</script> </binding> </button> <button> <legend>Close</legend> <key>Esc</key> <pref-width>47</pref-width> <binding> <command>dialog-close</command> </binding> </button> </group> </PropertyList>