- 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:
parent
dae87cdc0b
commit
269396c421
4 changed files with 133 additions and 270 deletions
165
Nasal/gui.nas
165
Nasal/gui.nas
|
@ -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);
|
||||
|
|
|
@ -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
127
gui/dialogs/tutorial.xml
Normal 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 <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>
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue