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>

Reply via email to