Howto talk:Implementing a simple GCA system
Jump to navigation
Jump to search
###
#
# UI code starts here
#
###
var configureGCA = func( gcaObject ) {
var (width,height) = (320,450);
var title = 'GCA Dialog ';
var window = canvas.Window.new([width,height],"dialog").set('title',title);
window.del = func()
{
print("Cleaning up window:",title,"\n");
call(canvas.Window.del, [], me);
};
# adding a canvas to the new window and setting up background colors/transparency
var myCanvas = window.createCanvas().set("background", canvas.style.getColor("bg_color"));
# creating the top-level/root group which will contain all other elements/group
var root = myCanvas.createGroup();
# create a new layout
var myLayout = canvas.VBoxLayout.new();
# assign it to the Canvas
myCanvas.setLayout(myLayout);
var setupWidgetTooltip = func(widget, tooltip) {
widget._view._root.addEventListener("mouseover", func gui.popupTip(tooltip) );
} # setupWidgetTooltip
var setupLabeledInput = func(root, layout, input) {
var label = canvas.gui.widgets.Label.new(root, canvas.style, {wordWrap: 0});
var unit_suffix = sprintf(" (%s):", input.unit);
label.setText(input.text~unit_suffix);
layout.addItem(label);
var field = canvas.gui.widgets.LineEdit.new(root, canvas.style, {});
layout.addItem(field);
field.setText(input.default_value);
if (input.focus)
field.setFocus();
setupWidgetTooltip(widget:field, tooltip: input.tooltip);
var el = field._view._root;
el.addEventListener("keypress", func (e) {
# colorize valid/invalid inputs
var color = (validationHelpers[input.validate]( field.text() ) == 0) ? [0,1,0] : [1,0,0];
field._view._root.setColorFill(color);
});
return field; # return to caller
} # setupLabeledInput()
var validationHelpers = {
_internalState: {},
'AircraftRoot': func(input) {
var root = props.getNode(input);
if (root == nil) return 1; # error
var required_props = ['altitude-ft', 'longitude-deg', 'latitude-deg'];
foreach(var rp; required_props) {
if (root.getNode(rp) == nil) return STATUS.FAILURE;
} # foreach
return STATUS.SUCCESS; # valid root node
},
'Airport': func(input) {
var match = airportinfo(input);
if (match == nil or typeof(match) != 'ghost') return STATUS.FAILURE;
validationHelpers._internalState.airport = match;
return STATUS.SUCCESS;
},
'Runway': func(input) {
var runways = validationHelpers._internalState.airport.runways;
if (typeof(runways)!="hash" or !size(keys(runways))) return STATUS.FAILURE;
if (runways[input] == nil) return STATUS.FAILURE;
return STATUS.SUCCESS;
},
'FinalApproach': func(input) {
return 0;
},
'Glidepath': func(input) {
return 0;
},
'SafetySlope': func(input) {
return 0;
},
'DecisionHeight': func(input) {
return 0;
},
'TouchdownOffset': func(input) {
return 0;
},
'TransmissionInterval': func(input) {
return 0;
},
'TransmissionProperty': func(input) {
return 0;
},
}; # validationHelpers;
var inputs = [
{text: 'Position root', default_value:'/position', focus:1, callback:GCAController.setPosition, tooltip:'property path to position node', validate: 'AircraftRoot', convert:nil, unit: 'property path'},
{text: 'Airport', default_value:'KSFO', focus:0, callback:GCAController.setAirport, tooltip:'ICAO ID, e.g. KSFO', validate: 'Airport', convert:nil, unit:'ICAO'},
{text: 'Runway', default_value:'28R', focus:0, callback:GCAController.setRunway, tooltip:'runway identifier, e.g. 28L', validate: 'Runway', convert:nil, unit:'rwy'},
{text: 'Final Approach', default_value:'10.00', focus:0, callback:GCAController.setFinalApproach, tooltip:'length of final approach leg', validate: 'FinalApproach', convert:nil, unit:'nm'},
{text: 'Glidepath', default_value:'3.00', focus:0, callback:GCAController.setGlidepath, tooltip:'glidepath in degrees, e.g. 3', validate: 'Glidepath', convert:nil, unit:'degrees'},
{text: 'Safety Slope', default_value:'2.00', focus:0, callback:GCAController.setSafetySlope, tooltip:'safety slope in degrees', validate: 'SafetySlope', convert:nil, unit:'degrees'},
{text: 'Decision Height', default_value:'200.00', focus:0, callback:GCAController.setDecisionHeight, tooltip:'decision height (vertical offset)', validate: 'DecisionHeight', convert:nil, unit:'ft'},
{text: 'Touchdown Offset', default_value:'0.00', focus:0, callback:GCAController.setTouchdownOffset, tooltip:'touchdown offset', validate: 'TouchdownOffset', convert:nil, unit:'m'},
{text: 'Transmission interval', default_value:'5.00', focus:0, callback:GCAController.setTransmissionInterval, tooltip:'Controller/timer resolution', validate: 'TransmissionInterval', convert:nil, unit:'secs'},
{text: 'Transmission channel', default_value:'/sim/messages/approach', focus:0, callback:GCAController.setTransmissionChannel, tooltip:'property to use for transmissions. For example: /sim/multiplay/chat or /sim/sound/voices/approach', validate: 'TransmissionProperty', convert:nil, unit:'property'},
]; # input fields
foreach(var input; inputs) {
# TODO: pass input hash
input.widget = setupLabeledInput(root, myLayout, input);
}
var validateFields = func() {
foreach(var f; inputs) {
var result = validationHelpers[f.validate] ( f.widget.text() );
if (result == STATUS.FAILURE) {
canvas.MessageBox.critical(
"Validation error",
"Error validating "~f.text,
cb = nil,
buttons = canvas.MessageBox.Ok
); # MessageBox
} # error handling
} # foreach
return STATUS.SUCCESS; # all validations passed
} # validateFields()
###
# global stuff
#
var gcaRunning = 0;
# var myGCA = nil;
var toggleFields = func(enabled) {
foreach(var i; inputs) {
i.widget.setEnabled(enabled);
}
}
var buildCGA = func() {
# create new object
# myGCA = GCAController.new();
foreach(var field; inputs) {
var value = field.widget.text();
# var converted_value = typeof(field.convert != nil) ? field.convert(value) : value;
# call vector
print(field.callback);
# http://wiki.flightgear.org/Nasal_library#call.28.29
call(GCAController[field.callback], [value], gcaObject );
}
# myGCA.start();
gcaObject.start();
}
var toggleGCA = func() {
if (gcaRunning) {
gcaObject.stop();
gcaRunning = 0;
toggleFields(!gcaRunning); # set editable
return;
}
if (!gcaRunning and validateFields()==0) {
gcaRunning = 1;
toggleFields(!gcaRunning); # set readonly
buildCGA();
return;
}
} # toggleGCA()
var button = canvas.gui.widgets.Button.new(root, canvas.style, {})
.setText("Start/Stop")
.setFixedSize(75, 25)
.listen("clicked", toggleGCA);
setupWidgetTooltip(widget:button, tooltip: "toggle GCA on/off");
myLayout.addItem(button);
}; # configureGCA();