20,741
edits
m (→Code: add an updated version) |
|||
Line 227: | Line 227: | ||
<syntaxhighlight lang="xml"><?xml version="1.0"?><?xml version="1.0"?> | <syntaxhighlight lang="xml"><?xml version="1.0"?><?xml version="1.0"?> | ||
<?xml version="1.0"?> | |||
<PropertyList> | <PropertyList> | ||
<name>canvas-nd</name> | <name>canvas-nd</name> | ||
Line 252: | Line 253: | ||
<command>dialog-apply</command> | <command>dialog-apply</command> | ||
</binding> | </binding> | ||
<!-- | <!-- | ||
<binding> | <binding> | ||
<command>dialog-toggle</command> | |||
</binding> | |||
</binding> | |||
--> | --> | ||
</checkbox> | </checkbox> | ||
Line 269: | Line 267: | ||
<group> | <group> | ||
<layout>vbox</layout> | <layout>vbox</layout> | ||
<!-- style selector (property will be overridden) --> | |||
<select> | |||
<label>Style</label> | |||
<name>unchanged</name> | |||
<property>WILL BE FILLED IN PROCEDURALLY</property> | |||
<binding> | |||
<command>dialog-apply</command> | |||
<object-name>unchanged</object-name> | |||
</binding> | |||
</select> | |||
<group> | <group> | ||
<layout>hbox</layout> | <layout>hbox</layout> | ||
Line 278: | Line 291: | ||
<halign>fill</halign> | <halign>fill</halign> | ||
<stretch>true</stretch> | <stretch>true</stretch> | ||
<!-- reducing the dimensions even more will look weird due to layouting issues, given the number of hbox aligned checkbox widgets --> | |||
<pref-width>400</pref-width> | <pref-width>400</pref-width> | ||
<pref-height>400</pref-height> | <pref-height>400</pref-height> | ||
Line 288: | Line 302: | ||
</canvas> | </canvas> | ||
<!-- ND/Canvas specific controls placed at the bottom of the display (e.g. range) --> | <!-- ND/Canvas specific controls placed at the bottom of the display (e.g. range) --> | ||
<group> | <group> <!-- move this down for better layout --> | ||
<layout>hbox</layout> | <layout>hbox</layout> | ||
<!-- | |||
<button> | <button> | ||
<legend>Cycle range</legend> | <legend>Cycle range</legend> | ||
Line 296: | Line 310: | ||
<binding> | <binding> | ||
<command>property-cycle</command> | <command>property-cycle</command> | ||
</binding> | |||
<value>10</value> | |||
<value>20</value> | |||
<value>40</value> | |||
<value>80</value> | |||
</button> | |||
--> | |||
<select> | |||
<label>Mode</label> | |||
<name>unchanged mode!!!</name> | |||
<property>WILL BE FILLED IN PROCEDURALLY</property> | |||
<binding> | |||
<command>dialog-apply</command> | |||
<object-name>unchanged mode !!!</object-name> | |||
</binding> | |||
</select> | |||
<!-- | |||
<select_> | |||
<label>VOR/ADF(l)</label> | |||
<name>a</name> | |||
<property>/a/c</property> | |||
<value>VOR</value> | |||
<value>OFF</value> | |||
<value>ADF</value> | |||
<binding> | |||
<command>dialog-apply</command> | |||
<object-name>a</object-name> | |||
</binding> | |||
</select_> | |||
--> | |||
<select> | |||
<name>unchanged range</name> | |||
<label>nm</label> | |||
<property>WILL BE FILLED IN PROCEDURALLY</property> | <property>WILL BE FILLED IN PROCEDURALLY</property> | ||
<binding> | |||
< | <command>dialog-apply</command> | ||
< | <object-name>unchanged range</object-name> | ||
</binding> | </binding> | ||
</select> | |||
<!-- | |||
<text> | <text> | ||
<format>Range: %s nm</format> | <format>Range: %s nm</format> | ||
<halign>left</halign> | |||
<property>WILL BE FILLED IN PROCEDURALLY</property> | <property>WILL BE FILLED IN PROCEDURALLY</property> | ||
<live>true</live> | <live>true</live> | ||
</text> | </text> | ||
--> | |||
<!-- | |||
<select_> | |||
<label>VOR/ADF(r)</label> | |||
<name>b</name> | |||
<property>/a/b</property> | |||
<value>VOR</value> | |||
<value>OFF</value> | |||
<value>ADF</value> | |||
<binding> | |||
<command>dialog-apply</command> | |||
<object-name>b</object-name> | |||
</binding> | |||
</select_> | |||
--> | |||
</group> | </group> | ||
Line 326: | Line 392: | ||
var getWidgetTemplate = func(identifier) { | var getWidgetTemplate = func(identifier) { | ||
var target = globals.gui.findElementByName(root, identifier ); | var target = globals.gui.findElementByName(root, identifier ); | ||
if(target == nil) die("Target node not found for | if(target == nil) die("Target node not found for identifier:"~identifier); | ||
return target; | return target; | ||
} | } | ||
var populateSelectWidget = func(widget, label, attribute, index, property, values) { | |||
# make up an identifier to be used for the object-name (fgcommands dialog-apply and -update) | |||
# TODO: change to read ND[x].attribute | |||
var objectName = "ND["~index~"]."~attribute; # attribute~index; | |||
widget.getNode("label",1).setValue(label); | |||
widget.getNode("name",1).setValue(objectName); | |||
widget.getNode("property",1).setValue(property); | |||
widget.getNode("binding/object-name",1).setValue(objectName); | |||
forindex(var c; values) { | |||
widget.getChild("value",c,1).setValue(values[c]); | |||
} | |||
}; | |||
## | |||
# store handles to each PUI/CanvasWidget canvas because cmdarg() may be invalid later on | |||
# but we cannot simply delete the ND entirely, because that would also delete the Canvas | |||
# so we keep a handle to the canvas region, and use that to instantiate a new ND | |||
var canvas_handles = {}; | |||
### | ### | ||
Line 337: | Line 424: | ||
var checkboxTemplate = getWidgetTemplate(root:cmdarg(), identifier:'checkbox-template'); | var checkboxTemplate = getWidgetTemplate(root:cmdarg(), identifier:'checkbox-template'); | ||
## | |||
## FIXME: this should be setting the style and reloading (closing/opening) the dialog | |||
## so that custom checkboxes can be supported ! | |||
## right now it's kinda pointless to show the Airbus style, because it's not using the same switches ... | |||
var initialize_nd = func(index) { | var initialize_nd = func(index) { | ||
# print("running init nd"); | |||
var identifier = "nd#"~index; | |||
var my_canvas = nil; | |||
# determine the canvas/placement to be used | |||
if (canvas_handles[identifier]==nil) { | |||
print("Storing Canvas handle"); | |||
my_canvas = canvas.get( cmdarg() ); | |||
canvas_handles[identifier] = my_canvas; | |||
} else { | |||
print("Retrieving stored Canvas handle"); | |||
my_canvas = canvas_handles[identifier]; | |||
} | |||
# debug.dump(my_canvas); | |||
# show_canvas_id(my_canvas); # this is for debugging only | # show_canvas_id(my_canvas); # this is for debugging only | ||
setupND(mfd_root: "/instrumentation/efis["~index~"]", my_canvas: my_canvas); | |||
var myND = setupND(mfd_root: "/instrumentation/efis["~index~"]", my_canvas: my_canvas, index:index); | |||
registerStyleChangeHandler(nd:myND.nd, index:index, my_canvas:my_canvas); | |||
}; | }; | ||
var registerStyleChangeHandler = func(nd, index, my_canvas) { | |||
# TODO: set up listener for styleSelector, clean up previous ND, re-run the init logic to apply the new style | |||
var l = setlistener("/gui/dialogs/canvas-nd/nd["~index~"]/selected-style",func() { | |||
var nd =nd; | |||
removelistener(l); | |||
# don't ask, there's something really weird going on with Nasal's naSubContext handling here | |||
settimer(func handleStyleChange(nd,index), 0.0); | |||
} ); # setlistener | |||
print("StyleChangeHandler registered"); | |||
} # registerStyleChangeHandler | |||
var handleStyleChange = func(nd, index) { | |||
print("Style change notification for ND #", index); | |||
nd.del(destroy_canvas:0); # don't actually destroy the Canvas yet, we're gonna reuse it | |||
initialize_nd(index:index); | |||
}; # handleStyleChange | |||
# not currently used, but could be used for validating the mfd/styles files before using them | |||
var errors = []; | var errors = []; | ||
Line 363: | Line 493: | ||
# debug.dump( NDStyles ); | # debug.dump( NDStyles ); | ||
print("ND Styles found:", size(keys(NDStyles))) | # TODO: this info could also be added to the GUI dialog | ||
print("Number of ND Styles found:", size(keys(NDStyles))); | |||
# to be used for shutting down each created instance upon closing the dialog (see the close block below) | # to be used for shutting down each created instance upon closing the dialog (see the close block below) | ||
Line 374: | Line 501: | ||
#### | #### | ||
## an adapted version of the setup logic found in ND.nas | ## an adapted version of the setup logic found in ND.nas | ||
## | ## FIXME: switches should be moved into the styles hash, after all they are highly aircraft specific | ||
var myCockpit_switches = { | var myCockpit_switches = { | ||
# symbolic alias : GUI legend/tooltip, relative property (as used in bindings), initial value, valid values (vector), property type | # symbolic alias : GUI legend/tooltip, relative property (as used in bindings), initial value, valid values (vector), property type | ||
Line 388: | Line 515: | ||
'toggle_traffic': {legend:'tfc', path: '/inputs/tfc',value:0, type:'BOOL'}, | 'toggle_traffic': {legend:'tfc', path: '/inputs/tfc',value:0, type:'BOOL'}, | ||
'toggle_centered': {legend:'ctr', path: '/inputs/nd-centered',value:0, type:'BOOL'}, | 'toggle_centered': {legend:'ctr', path: '/inputs/nd-centered',value:0, type:'BOOL'}, | ||
'toggle_lh_vor_adf': {legend:'vor/adf (l)', path: '/inputs/lh-vor-adf',value:0, values:[1, 0, 1 ], type:'INT'}, | 'toggle_lh_vor_adf': {legend:'vor/adf (l)', path: '/inputs/lh-vor-adf',value:0, values:[1, 0, -1 ], type:'INT'}, | ||
'toggle_rh_vor_adf': {legend:'vor/adf (r)', path: '/inputs/rh-vor-adf',value:0, values: [1, 0, 1 ], type:'INT'}, | 'toggle_rh_vor_adf': {legend:'vor/adf (r)', path: '/inputs/rh-vor-adf',value:0, values: [1, 0, -1 ], type:'INT'}, | ||
'toggle_display_mode': {legend:'map',path: '/mfd/display-mode', value:'MAP', values:['APP', 'MAP', 'PLAN', 'VOR' ], type:'STRING'}, | 'toggle_display_mode': {legend:'map',path: '/mfd/display-mode', value:'MAP', values:['APP', 'MAP', 'PLAN', 'VOR' ], type:'STRING'}, | ||
'toggle_display_type': {legend:'lcd',path: '/mfd/display-type', value:'LCD', values:['CRT', 'LCD' ], type:'STRING'}, | 'toggle_display_type': {legend:'lcd',path: '/mfd/display-type', value:'LCD', values:['CRT', 'LCD' ], type:'STRING'}, | ||
Line 406: | Line 533: | ||
return 'OFF'; | return 'OFF'; | ||
} | } | ||
var setupND = func(mfd_root, my_canvas, style='Airbus') | # TODO: make style configurable via property/listener | ||
var setupND = func(mfd_root, my_canvas, index) { | |||
var style = getprop("/gui/dialogs/canvas-nd/nd["~index~"]/selected-style") or 'Boeing'; | |||
print("Selected style for ND index #", index, " is: ", style); | |||
if (style == "Airbus") { | |||
print("NOTE: The Airbus style isnt yet well integrated, because it's using switches that are not used by the Boeing style, so this requires additional work, see the code for details"); | |||
} | |||
### | ### | ||
Line 419: | Line 554: | ||
# myCockpit_switches hash to map ND specific control properties | # myCockpit_switches hash to map ND specific control properties | ||
var myND= ND.new(mfd_root, myCockpit_switches, style); | var myND= ND.new(mfd_root, myCockpit_switches, style); | ||
#print("my_canvas is:"); | |||
#debug.dump(my_canvas); | |||
var group = my_canvas.createGroup(); | var group = my_canvas.createGroup(); | ||
myND.newMFD(group, my_canvas); | myND.newMFD(group, my_canvas); | ||
Line 425: | Line 561: | ||
# store the instance for later cleanup | # store the instance for later cleanup | ||
append(MFDInstances, myND); | append(MFDInstances, myND); | ||
# print("ND setup completed"); | |||
return {nd: myND, property_root: mfd_root}; | return {nd: myND, property_root: mfd_root}; | ||
} # setupND() | } # setupND() | ||
Line 432: | Line 569: | ||
{name: 'captain.ND', property_root:'/instrumentation/efis[0]',}, | {name: 'captain.ND', property_root:'/instrumentation/efis[0]',}, | ||
{name: 'copilot.ND', property_root:'/instrumentation/efis[1]',}, | {name: 'copilot.ND', property_root:'/instrumentation/efis[1]',}, | ||
{name: 'engineer.ND', property_root:'/instrumentation/efis[2]',}, | # you can add more entries below, for example: | ||
# {name: 'engineer.ND', property_root:'/instrumentation/efis[2]',}, | |||
]; | ]; | ||
Line 450: | Line 588: | ||
canvasWidget.getNode("text/label",1).setValue(c.name); | canvasWidget.getNode("text/label",1).setValue(c.name); | ||
canvasWidget.getNode("canvas/name").setValue(c.name); | canvasWidget.getNode("canvas/name").setValue(c.name); | ||
var StyleSelector = canvasWidget.getNode("select"); | |||
var style_property = "/gui/dialogs/canvas-nd/nd["~index~"]/selected-style"; | |||
populateSelectWidget(StyleSelector, "Style", "ndStyle", index, style_property, keys(NDStyles)); | |||
# add a single line of code to each canvas/nasal section setting up the ND instance | # add a single line of code to each canvas/nasal section setting up the ND instance | ||
canvasWidget.getNode("canvas/nasal/load").setValue("initialize_nd(index:"~index~");"); | canvasWidget.getNode("canvas/nasal/load").setValue("initialize_nd(index:"~index~");"); | ||
var RangeSelector = canvasWidget.getNode("group[1]/select[1]"); | |||
var range_property = c.property_root ~ '/inputs/range-nm'; | var range_property = c.property_root ~ '/inputs/range-nm'; | ||
populateSelectWidget(RangeSelector, "nm", "ndRangeNm", index, range_property, [10,20,40,80,160,320] ); | |||
var | # hook up the mode selector property | ||
var modeSelector = canvasWidget.getNode("group[1]/select"); | |||
var mode_property = "/instrumentation/efis["~index~"]/mfd/display-mode"; #FIXME: look up correct property via the style | |||
populateSelectWidget(modeSelector, "foo", "ndMode", index, mode_property, ['APP','PLAN','MAP','VOR']); | |||
var checkboxArea = getWidgetTemplate(root:canvasWidget, identifier:'mfd-controls'); | |||
var cb_index = 0; | var cb_index = 0; | ||
# add checkboxes for each boolean switch | # add checkboxes for each boolean switch | ||
# TODO: customize this for style-specific switches ! | |||
foreach(var s; keys(myCockpit_switches)) { | foreach(var s; keys(myCockpit_switches)) { | ||
var switch = s; | var switch = s; | ||
Line 473: | Line 618: | ||
checkbox.getNode("label").setValue(myCockpit_switches[switch].legend); | checkbox.getNode("label").setValue(myCockpit_switches[switch].legend); | ||
checkbox.getNode("property").setValue(c.property_root ~ myCockpit_switches[switch].path); | checkbox.getNode("property").setValue(c.property_root ~ myCockpit_switches[switch].path); | ||
var object_name = myCockpit_switches[switch].legend~cb_index; | |||
checkbox.getNode("name",1).setValue(object_name); | |||
checkbox.getNode("binding/object-name",1).setValue(object_name); | |||
#checkbox.getNode("binding/command/property-toggle/object-name",1).setValue(object_name); | |||
} # add checkboxes for each boolean ND switch | } # add checkboxes for each boolean ND switch | ||
index += 1; | index += 1; | ||
} # foreach ND instance | } # foreach ND instance | ||
#print("Complete dialog is:"); | |||
#props.dump( target ); | |||
]]> | ]]> | ||
</open> | </open> | ||
Line 503: | Line 654: | ||
<button> | <button> | ||
<legend>Reload</legend> | <legend>Reload</legend> | ||
<enable> | <enable> | ||
<property>/do-not-enable/reloading</property> | <property>/do-not-enable/reloading</property> | ||
Line 534: | Line 686: | ||
</button> | </button> | ||
</group> | </group> | ||
</PropertyList> | |||
</PropertyList> | </PropertyList> |