Howto:Prototyping a new NavDisplay Style: Difference between revisions

Jump to navigation Jump to search
m
→‎Code: add an updated version
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>property-toggle</command>
  <command>dialog-toggle</command>
<property>this will be customized</property>
  </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>
      <!-- TODO populate from values vector-->
    <binding>
       <value>10</value>
       <command>dialog-apply</command>
       <value>20</value>
       <object-name>unchanged range</object-name>
      <value>40</value>
      <value>80</value>
      <value>160</value>
      <value>320</value>
     </binding>   
     </binding>   
    </select>


    </button>
<!--
 
   <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 type:"~identifier);
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) {
                  var my_canvas = canvas.get( cmdarg() ); # this will get a handle to the parent canvas:
  # 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)));
var show_canvas_id = func(c) {
print("Canvas is:", c.getPath());
};


   # 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';
canvasWidget.getNode("group[1]/button/binding/property").setValue(range_property);
populateSelectWidget(RangeSelector, "nm", "ndRangeNm", index, range_property, [10,20,40,80,160,320] );
canvasWidget.getNode("group[1]/text/property").setValue(range_property);
# TODO: populate values dynamically with values vector from cockpit switches


var checkboxArea = getWidgetTemplate(root:canvasWidget, identifier:'mfd-controls'); # globals.gui.findElementByName(canvasWidget, 'mfd-controls');
# hook up the mode selector property
# if(checkboxArea == nil) die("Checkbox area node not found for mfd controls");
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>

Navigation menu