1
0
Fork 0

- remove tutorial dialogs from gui.nas and create a new XML dialog

$FG_ROOT/gui/dialogs/tutorial.xml. It combines the former selection
  and description dialogs, which makes browsing much more pleasant.
  No more switching back and forth between the two old dialogs. Also,
  the <list> box can handle a bigger number of tutorials than the
  <combo> box could.
- remove comments from the top of tutorial.nas. The documentation is
  now $FG_ROOT/Docs/README.tutorials
This commit is contained in:
mfranz 2007-03-24 20:56:40 +00:00
parent dae87cdc0b
commit 269396c421
4 changed files with 133 additions and 270 deletions

View file

@ -258,171 +258,6 @@ nextStyle = func {
dialog = {};
##
# Dynamically generate a tutorial dialog allowing the user to select a tutorial.
#
showSelTutDialog = func {
name = "selectTutorial";
title = "Select Tutorial";
#
# Immediately stop any tutorials that are running.
tutorial.stopTutorial();
#
# General Dialog Structure
#
dialog[name] = Widget.new();
dialog[name].set("name", name);
dialog[name].set("layout", "vbox");
header = dialog[name].addChild("text");
header.set("label", title);
dialog[name].addChild("hrule").set("pref-height", 1);
if (props.globals.getNode("/sim/tutorials") == nil) {
msg = dialog[name].addChild("text");
msg.set("label", "No tutorials available for this aircraft");
cancel = dialog[name].addChild("button");
cancel.set("legend", "Cancel");
cancel.setBinding("dialog-close");
fgcommand("dialog-new", dialog[name].prop());
showDialog(name);
return;
}
contentArea = dialog[name].addChild("group");
contentArea.set("layout", "hbox");
label = contentArea.addChild("text");
label.set("label", "Tutorial");
label.set("halign", "right");
combo = contentArea.addChild("combo");
combo.set("pref-width", "200");
combo.set("property", "/sim/tutorials/current-tutorial");
# Get a list of all tutorials
ltutorials = props.globals.getNode("/sim/tutorials").getChildren("tutorial");
for(i=0; i<size(ltutorials); i+=1) {
c = ltutorials[i];
if (c.getChild("name") != nil) {
lname = c.getChild("name").getValue();
lentry = combo.addChild("value");
lentry.prop().setValue(lname);
if (i == 0) {
setprop("/sim/tutorials/current-tutorial", lname);
}
}
}
buttonBar = dialog[name].addChild("group");
buttonBar.set("layout", "hbox");
buttonBar.set("default-padding", 10);
lcancel = buttonBar.addChild("button");
lcancel.set("legend", "Cancel");
lcancel.set("key", "esc");
lcancel.set("equal", 1);
lcancel.setBinding("dialog-close");
lnext = buttonBar.addChild("button");
lnext.set("legend", "Next");
lnext.set("default", 1);
lnext.set("equal", 1);
lnext.setBinding("dialog-apply");
lnext.setBinding("nasal", "gui.showTutorialDialog()");
lnext.setBinding("dialog-close");
# All done: pop it up
fgcommand("dialog-new", dialog[name].prop());
showDialog(name);
}
showTutorialDialog = func {
name = "displayTutorial";
# Get tutorial title and description
ltutorial = getprop("/sim/tutorials/current-tutorial");
if (ltutorial == nil) { ltutorial = "<undefined>"; }
lfound = 0;
ldescription = "No description available for this tutorial.";
foreach(c; props.globals.getNode("/sim/tutorials").getChildren("tutorial")) {
if (c.getChild("name").getValue() == ltutorial) {
lfound = 1;
if (c.getChild("description") != nil) {
ldescription = c.getChild("description") .getValue();
setprop("/sim/tutorials/description", ldescription);
}
}
}
title = "Tutorial \"" ~ ltutorial ~ "\"";
#
# General Dialog Structure
#
dialog[name] = Widget.new();
dialog[name].set("name", name);
dialog[name].set("layout", "vbox");
header = dialog[name].addChild("text");
header.set("label", title);
dialog[name].addChild("hrule").set("pref-height", 1);
if (lfound == 0) {
msg = dialog[name].addChild("text");
msg.set("label", "Unable to find tutorial " ~ ltutorial);
cancel = dialog[name].addChild("button");
cancel.set("legend", "Cancel");
cancel.setBinding("dialog-close");
fgcommand("dialog-new", dialog[name].prop());
showDialog(name);
return;
}
contentArea = dialog[name].addChild("group");
contentArea.set("layout", "hbox");
textarea = dialog[name].addChild("textbox");
textarea.set("pref-width", "600");
textarea.set("pref-height", "400");
textarea.set("slider", "20");
textarea.set("wrap", "true");
textarea.set("editable", "false");
textarea.set("valign", "top");
textarea.set("halign", "fill");
textarea.set("property", "/sim/tutorials/description");
buttonBar = dialog[name].addChild("group");
buttonBar.set("layout", "hbox");
buttonBar.set("default-padding", 10);
lback = buttonBar.addChild("button");
lback.set("legend", "Back");
lback.set("equal", 1);
lback.set("key", "esc");
lback.setBinding("nasal", "gui.showSelTutDialog()");
lback.setBinding("dialog-close");
lnext = buttonBar.addChild("button");
lnext.set("legend", "Start");
lnext.set("default", 1);
lnext.set("pref-width", 100);
lnext.set("equal", 1);
lnext.setBinding("nasal", "tutorial.startTutorial()");
lnext.setBinding("dialog-close");
# All done: pop it up
fgcommand("dialog-new", dialog[name].prop());
showDialog(name);
}
var setWeight = func(wgt, opt) {
var lbs = opt.getNode("lbs", 1).getValue();
wgt.getNode("weight-lb", 1).setValue(lbs);

View file

@ -1,104 +1,5 @@
# Functions for XML-based tutorials
# Code to process XML-based tutorials. See $FG_ROOT/Docs/README.tutorials
# ---------------------------------------------------------------------------------------
#
#
# Each tutorial consists of the following XML sections
#
# <name> - Tutorial Name
# <description> - description
# <audio-dir> - Optional: Directory to pick up audio instructions from.
# Relative to FG_ROOT
# <timeofday> - Optional: Time of day setting for tutorial:
# dawn/morning/noon/afternoon etc.
# <presets> - Optional: set of presets to used for start position.
# See commit-presets and gui/dialog/location-*.xml for details.
# <airport-id>
# <on-ground>
# <runway>
# <altitude-ft>
# <latitude-deg>
# <longitude-deg>
# <heading-deg>
# <airspeed-kt>
#
# <models>
# <model> - scenery object definition
# <path> - path to model (relative to $FG_ROOT)
# <longitude-deg>
# <latitude-deg>
# <elevation-ft>
# <heading-deg>
# <pitch-deg>
# <roll-deg
#
# <targets> - optional; define targets with arbitrary names
# <foo> - for each target "foo" the tutorial will keep properties
# <longitude-deg> /sim/tutorials/targets/foo/{direction-deg,distance-m}
# <latitude-deg> up-to-date
#
# <init> - Optional: Initialization section consist of one or more
# set nodes, and optionall a view node:
# <set>
# <property> - Property to set
# <value>/<property> - value or property to set from
# <view>
# <interval> - time in seconds until loop runs again (default: 5)
#
# <step> - Tutorial step - a segment of the tutorial, consisting of
# the following:
# <message> - Text instruction displayed when the tutorial reaches
# this step, and when neither the exit nor any error.
# criteria have been fulfilled
# <audio> - Optional: wav filename to play when displaying
# instruction
# <nasal><script>
# <marker>
# <view>
# <interval>
#
# <error> - Error conditions, causing error messages to be displayed.
# The tutorial doesn't advance while any error conditions are
# fulfilled. Consists of one or more check nodes:
# <message> - Error message to display if error criteria fulfilled.
# <audio> - Optional: wav filename to play when error condition fulfilled.
# <condition> - error condition (see $FG_ROOT/Docs/README.condition)
# <nasal><script>
# <interval>
#
# <exit> - Exit criteria causing tutorial to progress to next step.
# <condition> - exit condition (see $FG_ROOT/Docs/README.condition)
# <nasal><script>
# <view>
# <interval>
#
# <end>
# <message>> - Optional: Text to display when the tutorial exits the last step.
# <audio> - Optional: wav filename to play when the tutorial exits the last step
# <nasal><script>
# <set>
# <property>
# <value>/<property>
# <view>
# <interval>
#
#
# NOTE: everywhere where <message> and/or <audio> is supported there can be
# more than one <message> or <audio> entry defined, in which case one
# is randomly chosen.
# All <nasal><script> run in a separate Nasal namespace. There are a few
# functions pre-defined in this namespace:
# - next(n=1) ... switch to next step (default) or n steps forward
# - previous(n=1) ... switch to previous step (default) or n steps back
# - say("...", who="copilot", delay=1) ... say message, with optional
# speaker and delay. Available speakers are "pilot",
# "copilot", "atc", "ai-plane", "apprach", "ground".
# Examples: say("Look! There!");
# say("Oh, dear ...", "pilot", 2);
#
#
# GLOBAL VARIABLES
#
var STEP_INTERVAL = 5; # time between tutorial steps
@ -185,7 +86,7 @@ var startTutorial = func {
# Pick up any weather conditions/scenarios set
setprop("/environment/rebuild-layers", getprop("/environment/rebuild-layers") + 1);
settimer(func { stepTutorial(loop_id += 1) }, STEP_INTERVAL);
settimer(func { step_tutorial(loop_id += 1) }, STEP_INTERVAL);
}
@ -213,9 +114,9 @@ _setlistener("/sim/crashed", stopTutorial);
# increments an error counter
# - Otherwise display the instructions for the step.
#
var stepTutorial = func(id) {
var step_tutorial = func(id) {
id == loop_id or return;
var continue_after = func(i) { settimer(func { stepTutorial(id) }, i) }
var continue_after = func(i) { settimer(func { step_tutorial(id) }, i) }
if (current_step >= size(steps)) {
# end of the tutorial

127
gui/dialogs/tutorial.xml Normal file
View file

@ -0,0 +1,127 @@
<?xml version="1.0"?>
<PropertyList>
<name>tutorial</name>
<layout>vbox</layout>
<default-padding>4</default-padding>
<text>
<label>Select Tutorial</label>
</text>
<hrule><empty/></hrule>
<nasal>
<open>
tutorial.stopTutorial();
var list = cmdarg().getNode("group/group[1]/list");
var node = props.globals.getNode("/sim/tutorials", 1);
var tut = node.getChildren("tutorial");
var current = node.getNode("current-tutorial", 1);
# fill listbox
list.removeChildren("value");
forindex (var i; tut) {
var name = tut[i].getNode("name");
if (name == nil) {
die("tutorial #" ~ i ~ " has no &lt;name>");
}
name = name.getValue();
list.getChild("value", i, 1).setValue(name);
}
select = func {
var name = current.getValue();
foreach (var t; tut) {
if (t.getNode("name").getValue() == name) {
setprop("/sim/tutorials/current-description",
screen.trim(t.getNode("description").getValue()));
break;
}
}
fgcommand("dialog-update", props.Node.new({"object-name": "textbox",
"dialog-name": "tutorial"}));
}
if (current.getType() == "NONE" or current.getValue() == "") {
current.setValue(tut[0].getNode("name").getValue());
select();
}
</open>
</nasal>
<group>
<layout>hbox</layout>
<group>
<layout>vbox</layout>
<group>
<layout>vbox</layout>
<text>
<halign>left</halign>
<label></label>
<property>/sim/tutorials/current-tutorial</property>
<live>1</live>
</text>
</group>
<textbox>
<name>textbox</name>
<halign>fill</halign>
<pref-width>600</pref-width>
<pref-height>480</pref-height>
<slider>20</slider>
<wrap>true</wrap>
<editable>false</editable>
<live>1</live>
<property>/sim/tutorials/current-description</property>
</textbox>
</group>
<group>
<layout>vbox</layout>
<list>
<name>list</name>
<halign>fill</halign>
<pref-width>170</pref-width>
<pref-height>400</pref-height>
<property>/sim/tutorials/current-tutorial</property>
<binding>
<command>dialog-apply</command>
<object-name>list</object-name>
</binding>
<binding>
<command>nasal</command>
<script>select()</script>
</binding>
</list>
<empty><stretch>true</stretch></empty>
<button>
<legend>Cancel</legend>
<equal>true</equal>
<key>Esc</key>
<binding>
<command>dialog-close</command>
</binding>
</button>
<button>
<legend>Start Tutorial</legend>
<equal>true</equal>
<default>true</default>
<binding>
<command>nasal</command>
<script>tutorial.startTutorial()</script>
</binding>
<binding>
<command>dialog-close</command>
</binding>
</button>
</group>
</group>
</PropertyList>

View file

@ -494,8 +494,8 @@
<label>Start Tutorial</label>
<name>tutorial-start</name>
<binding>
<command>nasal</command>
<script>gui.showSelTutDialog()</script>
<command>dialog-show</command>
<dialog-name>tutorial</dialog-name>
</binding>
</item>