- gui.OverlaySelector: add optional sort criterion, add selection by index,
add next(), and previous() methods. - aircraft.nas: deprecate formation class
This commit is contained in:
parent
6a72126327
commit
5d576f0e3d
3 changed files with 112 additions and 104 deletions
|
@ -3,7 +3,6 @@
|
|||
|
||||
|
||||
|
||||
|
||||
# constants
|
||||
# ==============================================================================
|
||||
var D2R = math.pi / 180;
|
||||
|
@ -11,7 +10,6 @@ var R2D = 180 / math.pi;
|
|||
|
||||
|
||||
|
||||
|
||||
# helper functions
|
||||
# ==============================================================================
|
||||
|
||||
|
@ -26,18 +24,14 @@ var makeNode = func(n) {
|
|||
}
|
||||
|
||||
|
||||
# returns arg[1]-th optional argument of vector arg[0] or default value arg[2]
|
||||
# returns args[index] if available and non-nil, or default otherwise
|
||||
#
|
||||
var optarg = func {
|
||||
if (size(arg[0]) > arg[1] and arg[0][arg[1]] != nil)
|
||||
arg[0][arg[1]];
|
||||
else
|
||||
arg[2];
|
||||
var optarg = func(args, index, default) {
|
||||
size(args) > index and args[index] != nil ? args[index] : default;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
# door
|
||||
# ==============================================================================
|
||||
# class for objects moving at constant speed, with the ability to
|
||||
|
@ -189,10 +183,8 @@ var light = {
|
|||
# light.switch(bool) -> set light switch (also affects other lights
|
||||
# that use the same switch)
|
||||
switch: func(v) { me.switchN.setBoolValue(v); me },
|
||||
|
||||
# light.toggle() -> toggle light switch
|
||||
toggle: func { me.switchN.setBoolValue(!me.switchN.getValue()); me },
|
||||
|
||||
# light.cont() -> continuous light
|
||||
cont: func {
|
||||
if (!me.continuous) {
|
||||
|
@ -202,7 +194,6 @@ var light = {
|
|||
}
|
||||
me;
|
||||
},
|
||||
|
||||
# light.blink() -> blinking light (default)
|
||||
# light.blink(3) -> when switched on, only run three blink sequences;
|
||||
# second optional arg defines state after the sequences
|
||||
|
@ -217,7 +208,6 @@ var light = {
|
|||
}
|
||||
me;
|
||||
},
|
||||
|
||||
_switch_: func {
|
||||
var switch = me.switchN.getBoolValue();
|
||||
switch != me.lastswitch or return;
|
||||
|
@ -232,7 +222,6 @@ var light = {
|
|||
me._loop_(me.loopid);
|
||||
}
|
||||
},
|
||||
|
||||
_loop_: func(id) {
|
||||
id == me.loopid or return;
|
||||
if (!me.count) {
|
||||
|
@ -251,6 +240,7 @@ var light = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
# lowpass
|
||||
# ==============================================================================
|
||||
# class that implements a variable-interval EWMA (Exponentially Weighted
|
||||
|
@ -293,6 +283,7 @@ var lowpass = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
# angular lowpass
|
||||
# ==============================================================================
|
||||
# same as above, but for angles. Filters sin/cos separately and calculates the
|
||||
|
@ -321,6 +312,7 @@ var angular_lowpass = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
# data
|
||||
# ==============================================================================
|
||||
# class that loads and saves properties to aircraft-specific data files in
|
||||
|
@ -408,6 +400,7 @@ var data = {
|
|||
};
|
||||
|
||||
|
||||
|
||||
# timer
|
||||
# ==============================================================================
|
||||
# class that implements timer that can be started, stopped, reset, and can
|
||||
|
@ -522,7 +515,7 @@ var livery = {
|
|||
if (me.dir[-1] != `/`)
|
||||
me.dir ~= "/";
|
||||
me.name_path = name_path;
|
||||
me.sort_path = sort_path != nil ? sort_path : name_path;
|
||||
me.sort_path = sort_path or name_path;
|
||||
me.rescan();
|
||||
aircraft.data.add(name_path);
|
||||
me.dialog = gui.Dialog.new("livery-select");
|
||||
|
@ -634,7 +627,7 @@ var livery_update = {
|
|||
|
||||
|
||||
|
||||
# formation
|
||||
# formation *** DEPRECATED ***
|
||||
# =============================================================================
|
||||
# A modification of the livery class. This class maintains formation
|
||||
# XML files (see Blackburn Buccaneer for an example). Files are regular
|
||||
|
@ -690,6 +683,7 @@ var formation = {
|
|||
},
|
||||
# select by index (out-of-bounds indices are wrapped)
|
||||
set: func(i) {
|
||||
print("***\n*** use of aircraft.formation is deprecated; use gui.OverlaySelector\n***");
|
||||
if (i < 0)
|
||||
i = size(me.data) - 1;
|
||||
if (i >= size(me.data))
|
||||
|
@ -1016,7 +1010,6 @@ var HUD = {
|
|||
# module initialization
|
||||
# ==============================================================================
|
||||
#
|
||||
|
||||
_setlistener("/sim/signals/nasal-dir-initialized", func {
|
||||
props.initNode("/sim/time/delta-sec", 0);
|
||||
props.initNode("/sim/time/delta-realtime-sec", 0.00000001);
|
||||
|
|
187
Nasal/gui.nas
187
Nasal/gui.nas
|
@ -214,7 +214,7 @@ var Dialog = {
|
|||
if (m.prop.getName() != "dialog")
|
||||
die("Dialog class: node name must end with '/dialog'");
|
||||
|
||||
m.listener = setlistener("/sim/signals/reinit-gui", func { m.load() }, 1);
|
||||
m.listener = setlistener("/sim/signals/reinit-gui", func m.load(), 1);
|
||||
}
|
||||
return Dialog.instance[m.name] = m;
|
||||
},
|
||||
|
@ -264,6 +264,106 @@ var Dialog = {
|
|||
};
|
||||
|
||||
|
||||
##
|
||||
# Overlay selector. Displays a list of overlay XML files and copies the
|
||||
# chosen one to the property tree. The class allows to select liveries,
|
||||
# insignia, decals, variants, etc. Usually the overlay properties are
|
||||
# fed to "select" and "material" animations.
|
||||
#
|
||||
# SYNOPSIS:
|
||||
# OverlaySelector.new(<title>, <dir>, <nameprop> [, <sortprop> [, <callback>]]);
|
||||
#
|
||||
# title ... dialog title
|
||||
# dir ... directory where to find the XML overlay files,
|
||||
# relative to FG_ROOT
|
||||
# nameprop ... property in an overlay file that contains the name
|
||||
# The result is written to this property in the
|
||||
# property tree. Attach a listener to this property
|
||||
# if you want changes reported.
|
||||
# sortprop ... property in an overlay file that should be used
|
||||
# as sorting criterion, if alphabetic sorting by
|
||||
# name is undesirable
|
||||
# callback ... callback function
|
||||
#
|
||||
# EXAMPLE:
|
||||
# aircraft.data.add("sim/model/pilot"); # autosave the pilot
|
||||
# var pilots_dialog = gui.OverlaySelector.new("Pilots",
|
||||
# "Aircraft/foo/Models/Pilots",
|
||||
# "sim/model/pilot");
|
||||
#
|
||||
# pilots_dialog.open(); # or ... close(), or toggle()
|
||||
#
|
||||
#
|
||||
var OverlaySelector = {
|
||||
new: func(title, dir, nameprop, sortprop = nil, callback = nil) {
|
||||
var name = "overlay-select-";
|
||||
var data = props.globals.getNode("/sim/gui/dialogs/", 1);
|
||||
var i = nil;
|
||||
for (i = 1; 1; i += 1)
|
||||
if (data.getNode(name ~ i, 0) == nil)
|
||||
break;
|
||||
data = data.getNode(name ~= i, 1);
|
||||
|
||||
var m = Dialog.new(data.getNode("dialog", 1), "gui/dialogs/overlay-select.xml", name);
|
||||
m.parents = [OverlaySelector, Dialog];
|
||||
|
||||
m.dir = getprop("/sim/fg-root") ~ "/" ~ dir;
|
||||
if (m.dir[-1] != `/`)
|
||||
m.dir ~= '/';
|
||||
m.nameprop = nameprop;
|
||||
m.sortprop = sortprop or nameprop;
|
||||
m.callback = callback or func { nil };
|
||||
m.result = props.initNode(data.getNode("result", 1), "");
|
||||
m.listener = setlistener(m.result, func(n) m.select(n.getValue()));
|
||||
|
||||
m.prop.getNode("group/text/label").setValue(title);
|
||||
m.list = m.prop.getNode("list");
|
||||
m.list.getNode("property").setValue(m.result.getPath());
|
||||
|
||||
m.rescan();
|
||||
m.select(getprop(m.nameprop) or "");
|
||||
return m;
|
||||
},
|
||||
del: func {
|
||||
removelistener(me.listener);
|
||||
me.data.remove();
|
||||
},
|
||||
rescan: func {
|
||||
me.data = [];
|
||||
foreach (var file; directory(me.dir)) {
|
||||
if (substr(file, -4) != ".xml")
|
||||
continue;
|
||||
var n = io.read_properties(me.dir ~ file);
|
||||
var name = n.getNode(me.nameprop, 1).getValue() or "[NO NAME]";
|
||||
var index = n.getNode(me.sortprop, 1).getValue() or 0;
|
||||
append(me.data, [name, index, n.getValues()]);
|
||||
me.data = sort(me.data, func(a, b) num(a[1]) == nil or num(b[1]) == nil
|
||||
? cmp(a[1], b[1]) : a[1] - b[1]);
|
||||
}
|
||||
|
||||
me.list.removeChildren("value");
|
||||
forindex (var i; me.data)
|
||||
me.list.getChild("value", i, 1).setValue(me.data[i][0]);
|
||||
},
|
||||
set: func(index) {
|
||||
me.current = math.mod(index, size(me.data));
|
||||
props.globals.setValues(me.data[me.current][2]);
|
||||
me.callback(me.data[me.current]);
|
||||
},
|
||||
select: func(name) {
|
||||
forindex (var i; me.data)
|
||||
if (me.data[i][0] == name)
|
||||
me.set(i);
|
||||
},
|
||||
next: func {
|
||||
me.set(me.current + 1);
|
||||
},
|
||||
previous: func {
|
||||
me.set(me.current - 1);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
##
|
||||
# FileSelector class (derived from Dialog class).
|
||||
#
|
||||
|
@ -398,91 +498,6 @@ settimer(func {
|
|||
}, 0);
|
||||
|
||||
|
||||
##
|
||||
# Overlay selector. Displays a list of overlay XML files and copies the
|
||||
# chosen one to the property tree. The class allows to select liveries,
|
||||
# insignia, decals, variants, etc. Usually the overlay properties are
|
||||
# fed to "select" and "material" animations.
|
||||
#
|
||||
# SYNOPSIS:
|
||||
# OverlaySelector.new(<title>, <dir>, <nameprop>);
|
||||
#
|
||||
# title ... dialog title
|
||||
# dir ... directory where to find the XML overlay files,
|
||||
# relative to FG_ROOT
|
||||
# nameprop ... property in an overlay file that contains the name
|
||||
# The result is written to this property in the
|
||||
# property tree. You can attach a listener here to
|
||||
# get changes reported.
|
||||
#
|
||||
# EXAMPLE:
|
||||
# aircraft.data.add("sim/model/pilot"); # autosave the pilot
|
||||
# var pilots_dialog = gui.OverlaySelector.new("Pilots",
|
||||
# "Aircraft/foo/Models/Pilots",
|
||||
# "sim/model/pilot");
|
||||
#
|
||||
# pilots_dialog.open(); # or ... close(), or toggle()
|
||||
#
|
||||
#
|
||||
var OverlaySelector = {
|
||||
new: func(title, dir, nameprop) {
|
||||
var name = "overlay-select-";
|
||||
var data = props.globals.getNode("/sim/gui/dialogs/", 1);
|
||||
var i = nil;
|
||||
for (i = 1; 1; i += 1)
|
||||
if (data.getNode(name ~ i, 0) == nil)
|
||||
break;
|
||||
data = data.getNode(name ~= i, 1);
|
||||
|
||||
var m = Dialog.new(data.getNode("dialog", 1), "gui/dialogs/overlay-select.xml", name);
|
||||
m.parents = [OverlaySelector, Dialog];
|
||||
|
||||
m.dir = getprop("/sim/fg-root") ~ "/" ~ dir;
|
||||
m.nameprop = nameprop;
|
||||
m.result = data.getNode("result", 1);
|
||||
m.cblistener = setlistener(m.result, func m.set());
|
||||
|
||||
m.prop.getNode("group/text/label").setValue(title);
|
||||
m.list = m.prop.getNode("list");
|
||||
m.list.getNode("property").setValue(m.result.getPath());
|
||||
|
||||
m.rescan();
|
||||
m.set(getprop(m.nameprop));
|
||||
return m;
|
||||
},
|
||||
del: func {
|
||||
removelistener(me.cblistener);
|
||||
me.data.remove();
|
||||
},
|
||||
rescan: func {
|
||||
me.options = [];
|
||||
foreach (var file; directory(me.dir)) {
|
||||
if (substr(file, -4) != ".xml")
|
||||
continue;
|
||||
var n = io.read_properties(me.dir ~ "/" ~ file);
|
||||
var name = n.getNode(me.nameprop, 1).getValue() or "[NO NAME]";
|
||||
append(me.options, [name, n.getValues()]);
|
||||
me.options = sort(me.options, func(a, b) cmp(a[0], b[0]));
|
||||
}
|
||||
|
||||
me.list.removeChildren("value");
|
||||
forindex (var i; me.options)
|
||||
me.list.getChild("value", i, 1).setValue(me.options[i][0]);
|
||||
},
|
||||
set: func(which = nil) {
|
||||
if (!size(me.options))
|
||||
return;
|
||||
var choice = which or me.result.getValue() or me.options[0][0];
|
||||
foreach (var o; me.options) {
|
||||
if (o[0] == choice) {
|
||||
props.globals.setValues(o[1]);
|
||||
setprop(me.nameprop, choice);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
##
|
||||
# Apply whole dialog or list of widgets. This copies the widgets'
|
||||
# visible contents to the respective <property>.
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
# "ALIAS" return from getType to detect them (to avoid cycles while
|
||||
# walking the tree).
|
||||
#
|
||||
Node = {
|
||||
var Node = {
|
||||
getType : func { wrap(_getType(me._g, arg)) },
|
||||
getAttribute : func { wrap(_getAttribute(me._g, arg)) },
|
||||
setAttribute : func { wrap(_setAttribute(me._g, arg)) },
|
||||
|
|
Loading…
Add table
Reference in a new issue