From 6b1db73423baa612f6bf663dca9b10aecc48ec27 Mon Sep 17 00:00:00 2001 From: Stuart Buchanan Date: Tue, 12 Mar 2019 20:33:35 +0000 Subject: [PATCH] FG1000 - Improved timers Implement single timer for all highlighting of elements. --- .../Instruments-3d/FG1000/Nasal/MFDPage.nas | 15 ++- .../Nasal/MFDPages/Surround/Surround.nas | 104 +++++++++--------- .../Instruments-3d/FG1000/fg1000-multikey.xml | 35 ------ Nasal/canvas/PFD/DataEntryElement.nas | 86 ++++++--------- Nasal/canvas/PFD/HighlightElement.nas | 27 ++--- Nasal/canvas/PFD/HighlightTimer.nas | 46 ++++++++ Nasal/canvas/PFD/TextElement.nas | 55 +++++---- 7 files changed, 179 insertions(+), 189 deletions(-) create mode 100644 Nasal/canvas/PFD/HighlightTimer.nas diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas index 8d9e63c66..1ff3254cc 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPage.nas @@ -22,6 +22,7 @@ var loadPFDFile = func(file) io.load_nasal(mfd_dir ~ file, "PFD"); loadPFDFile("DefaultStyle.nas"); loadPFDFile("UIElement.nas"); +loadPFDFile("HighlightTimer.nas"); loadPFDFile("TextElement.nas"); loadPFDFile("HighlightElement.nas"); loadPFDFile("GroupElement.nas"); @@ -92,13 +93,13 @@ getElement : func(e) { addTextElements : func(symbols, style=nil) { foreach (var s; symbols) { - me._textElements[s] = PFD.TextElement.new(me.pageName, me._SVGGroup, s, style); + me._textElements[s] = PFD.TextElement.new(me.pageName, me._SVGGroup, s, "", style); } }, addTextElement : func(e, style=nil) { if (me._textElements[e] == nil) { - me._textElements[e] = PFD.TextElement.new(me.pageName, me._SVGGroup, e, style); + me._textElements[e] = PFD.TextElement.new(me.pageName, me._SVGGroup, e, "", style); } else { die("addTextElement element already exists: "~ me.pageName ~ e); } @@ -108,8 +109,8 @@ getTextElement : func(symbolName) { return me._textElements[symbolName]; }, -highlightTextElement : func(symbolName) { - me._textElements[symbolName].highlightElement(); +highlightTextElement : func(symbolName, highlightime=nil) { + me._textElements[symbolName].highlightElement(highlightime); }, unhighlightTextElement : func(symbolName) { @@ -129,6 +130,12 @@ setTextElement : func(symbolName, value) { sym.setValue(value); }, +setTextElements : func(symbols, value) { + foreach (var s; symbols) { + me.setTextElement(s, value); + } +}, + setTextElementLat : func(symbolName, value) { if ((value == nil) or (int(value) == nil)) { me.setTextElement(symbolName, "_ __°__.__'"); diff --git a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/Surround/Surround.nas b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/Surround/Surround.nas index 4695e9a8c..9174335e0 100644 --- a/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/Surround/Surround.nas +++ b/Aircraft/Instruments-3d/FG1000/Nasal/MFDPages/Surround/Surround.nas @@ -114,17 +114,20 @@ var Surround = "Nav1ID", "Nav2ID", ]; + var fdTextElements = ["HeaderAPLateralArmed", "HeaderAPLateralActive", "HeaderAPVerticalArmed", "HeaderAPVerticalActive", "HeaderAPVerticalReference"]; + obj.addTextElements(textElements); if (pfd) { obj.addTextElements(["HeaderFrom", "HeaderTo", "LegDistance", "LegBRG"]); - obj.addTextElements(["HeaderAPLateralArmed", "HeaderAPLateralActive", "HeaderAPVerticalArmed", "HeaderAPVerticalActive", "HeaderAPVerticalReference"], FD_STATUS_STYLE); + obj.addTextElements(fdTextElements, FD_STATUS_STYLE); + obj.setTextElements(fdTextElements, ""); obj._apStatus = PFD.TextElement.new(obj.pageName, svg, "HeaderAPStatus", "", AP_STATUS_STYLE); - obj._apStatusTimer = nil; - obj._apLateralStatusTimer = nil; - obj._apVerticalStatusTimer = nil; obj._dto = PFD.HighlightElement.new(obj.pageName, svg, "HeaderDTO", "DTO"); obj._leg = PFD.HighlightElement.new(obj.pageName, svg, "HeaderActiveLeg", "Leg"); + obj._old_lateral_armed = nil; # We store the previous armed values so we can detect a transtion from armed to active. + obj._old_vertical_armed = nil; + obj._ap_on = 0; } else { obj.addTextElements(["Header1Label", "Header1Value", "Header2Label", "Header2Value", @@ -280,54 +283,63 @@ var Surround = } } - # When the Autopilot Heading or Altitude modes change we flash the appropriate annunicator for 10 seconds. + # When the Autopilot Heading or Altitude modes moves from armed to active we flash the appropriate annunicator for 10 seconds. + # Unfortunately as we use a TriggeredPropertyPublisher, we won't have both the HeaderAP[Vertical|Lateral]Active and HeaderAP[Vertical|Lateral]Armed + # values at the same time so have to save off any change in the armed values to check against. + if ((data["AutopilotHeadingMode"] != nil) and (data["AutopilotHeadingMode"] != me.getTextValue("HeaderAPLateralActive"))) { + me.setTextElement("HeaderAPLateralActive", data["AutopilotHeadingMode"]); - # This code crashes FG at present. No idea why - perhaps reference to "me" in maketimer? - #me.highlightTextElement("HeaderAPLateralActive"); - #if (me._apLateralStatusTimer == nil) me._apLateralStatusTimer = maketimer(10.0, me, me._stopLateralStatusFlashing); - #me._apLateralStatusTimer.singleShot = 1; - #me._apLateralStatusTimer.restart(10.0); + + if ((data["AutopilotHeadingMode"] != "") and + ((data["AutopilotHeadingMode"] == me._old_lateral_armed) or + (data["AutopilotHeadingMode"] == me.getTextValue("HeaderAPLateralArmed")))) + { + # Transition from an armed mode to a new mode, so flash + me.highlightTextElement("HeaderAPLateralActive", 10); + } } - # When the Autopilot Heading or Altitude modes change we flash the appropriate annunicator for 10 seconds. if ((data["AutopilotAltitudeMode"] != nil) and (data["AutopilotAltitudeMode"] != me.getTextValue("HeaderAPVerticalActive"))) { + me.setTextElement("HeaderAPVerticalActive", data["AutopilotAltitudeMode"]); - # This code crashes FG at present. No idea why - perhaps reference to "me" in maketimer? - #me.highlightTextElement("HeaderAPVerticalActive"); - #if (me._apVerticalStatusTimer == nil) me._apVerticalStatusTimer = maketimer(10.0, me, me._stopVerticalStatusFlashing); - #me._apVerticalStatusTimer.singleShot = 1; - #me._apVerticalStatusTimer.restart(10.0); + + if ((data["AutopilotAltitudeMode"] != "") and + ((data["AutopilotAltitudeMode"] == me._old_vertical_armed) or + (data["AutopilotAltitudeMode"] == me.getTextValue("HeaderAPVerticalArmed")))) + { + # Transition from an armed mode to a new mode, so flash + me.highlightTextElement("HeaderAPVerticalActive", 10); + } } - if (data["AutopilotHeadingModeArmed"] != nil) me.setTextElement("HeaderAPLateralArmed", data["AutopilotHeadingModeArmed"]); - if (data["AutopilotAltitudeModeArmed"] != nil) me.setTextElement("HeaderAPVerticalArmed", data["AutopilotAltitudeModeArmed"]); + if (data["AutopilotHeadingModeArmed"] != nil) { + if (data["AutopilotHeadingModeArmed"] != me.getTextValue("HeaderAPLateralArmed")) me._old_lateral_armed = me.getTextValue("HeaderAPLateralArmed"); + me.setTextElement("HeaderAPLateralArmed", data["AutopilotHeadingModeArmed"]); + } + + if (data["AutopilotAltitudeModeArmed"] != nil) { + if (data["AutopilotAltitudeModeArmed"] != me.getTextValue("HeaderAPVerticalArmed")) me._old_lateral_armed = me.getTextValue("HeaderAPVerticalArmed"); + me.setTextElement("HeaderAPVerticalArmed", data["AutopilotAltitudeModeArmed"]); + } # When the Autopilot is disengaged, the AP status element flashes for 5 seconds before disappearing if (data["AutopilotEnabled"] != nil) { - if (data["AutopilotEnabled"] == 1) { - me._apStatus.setValue("AP"); - if (me._apStatusTimer != nil) { - # We were previously flashing, so stop. - me._apStatusTimer.stop(); - me._apStatusTimer = nil; - me._apStatus.unhighlightElement(); - } - } else { - if ((me._apStatus.getValue() != "") and (me._apStatus.isHighlighted() == 0)) { - # We were previously enabled, so we want to make the AP Status element - # flash for 5 seconds before removing it. The highlightElement() - # starts it flashing, and we use a timer to stop it. - # This code crashes FG at present. No idea why - perhaps reference to "me" in maketimer? - #me._apStatus.highlightElement(); - #if (me._apStatusTimer == nil) me._apStatusTimer = maketimer(5.0, me, me._stopAPStatusFlashing); - #me._apStatusTimer.singleShot = 1; - #me._apStatusTimer.restart(5.0); - me._apStatus.setValue(""); - } + if ((data["AutopilotEnabled"] == 1) and (me._ap_on == 0)) { + # Toggle the AP on, stopping any flashing that might be occurring. + me._apStatus.unhighlightElement(); + me._apStatus.setValue("AP"); + me._ap_on = 1; + } + + if ((data["AutopilotEnabled"] == 0) and me._ap_on) { + # Toggle the AP off, by flashing the AP Status element for 5 seconds before removing it. + # Only do this if we're not already flashing. + me._ap_on = 0; + me._apStatus.highlightElement(5.0, ""); } } @@ -370,22 +382,6 @@ var Surround = } }, - _stopLateralStatusFlashing : func() - { - me.unhighlightTextElement("HeaderAPLateralActive"); - }, - - _stopVerticalStatusFlashing : func() - { - me.unhighlightTextElement("HeaderAPVerticalActive"); - }, - - _stopAPStatusFlashing : func() - { - me._apStatus.unhighlightElement(); - me._apStatus.setValue(""); - }, - getCurrentPage : func() { var currentpage = PAGE_GROUPS[me._selectedPageGroup].pages[me._selectedPage]; diff --git a/Aircraft/Instruments-3d/FG1000/fg1000-multikey.xml b/Aircraft/Instruments-3d/FG1000/fg1000-multikey.xml index 7ddb8bce2..a17d94b49 100644 --- a/Aircraft/Instruments-3d/FG1000/fg1000-multikey.xml +++ b/Aircraft/Instruments-3d/FG1000/fg1000-multikey.xml @@ -291,25 +291,6 @@ - - s - MFD String input - - %s - MFD String input : %s - - nasal - - - - - a Airport Information @@ -358,22 +339,6 @@ - - n - Nearest Airport - - - nasal - - - - m Map diff --git a/Nasal/canvas/PFD/DataEntryElement.nas b/Nasal/canvas/PFD/DataEntryElement.nas index ff9193b00..96dee8600 100644 --- a/Nasal/canvas/PFD/DataEntryElement.nas +++ b/Nasal/canvas/PFD/DataEntryElement.nas @@ -34,13 +34,10 @@ var DataEntryElement = } # State and timer for flashing highlighting of elements - obj._highlighted = 0; obj._highlightEnabled = 0; - obj._flashTimer = nil; - - obj._highlightChar = 0; + obj._flash = 0; obj._highlightCharEnabled = 0; - obj._flashCharTimer = nil; + obj._flashChar = 0; return obj; }, @@ -59,72 +56,57 @@ var DataEntryElement = }, _flashElement : func() { - if (me._highlightEnabled == 0) { - me._symbol.setDrawMode(canvas.Text.TEXT); - me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); - me._highlighted = 0; - } else { - if (me._highlighted == 0) { + if ((me._highlightEnabled == 1) and (me._highlightCharEnabled == 0)) { + if (me._flash == 0) { me._symbol.setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX); me._symbol.setColorFill(me._style.HIGHLIGHT_COLOR); me._symbol.setColor(me._style.HIGHLIGHT_TEXT_COLOR); - me._highlighted = 1; + me._flash = 1; } else { me._symbol.setDrawMode(canvas.Text.TEXT); me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); - me._highlighted = 0; + me._flash = 0; + } + } + + if (me._highlightCharEnabled == 1) { + if (me._flashChar == 0) { + me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX); + me._dataEntrySymbol[me._dataEntryPos].setColorFill(me._style.HIGHLIGHT_COLOR); + me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.HIGHLIGHT_TEXT_COLOR); + me._flashChar = 1; + } else { + me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT); + me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.NORMAL_TEXT_COLOR); + me._flashChar = 0; } } }, highlightElement : func() { me._highlightEnabled = 1; - me._highlighted = 0; - me._flashElement(); - me._flashTimer = maketimer(me._style.CURSOR_BLINK_PERIOD, me, me._flashElement); - me._flashTimer.start(); + me._flash = 0; + PFD.HighlightTimer.startHighlight(me, -1); }, unhighlightElement : func() { - if (me._flashTimer != nil) me._flashTimer.stop(); - me._flashTimer = nil; me._highlightEnabled = 0; - me._highlighted = 0; - me._flashElement(); + me._symbol.setDrawMode(canvas.Text.TEXT); + me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); + PFD.HighlightTimer.stopHighlight(me); }, - _flashCharElement : func() { - if (me._highlightCharEnabled == 0) { - me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT); - me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.NORMAL_TEXT_COLOR); - me._highlightChar = 0; - } else { - if (me._highlightChar == 0) { - me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX); - me._dataEntrySymbol[me._dataEntryPos].setColorFill(me._style.HIGHLIGHT_COLOR); - me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.HIGHLIGHT_TEXT_COLOR); - me._highlightChar = 1; - } else { - me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT); - me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.NORMAL_TEXT_COLOR); - me._highlightChar = 0; - } - } - }, - highlightCharElement : func() { + _highlightCharElement : func() { me._highlightCharEnabled = 1; - me._highlightChar = 0; - me._flashCharElement(); - me._flashCharTimer = maketimer(me._style.CURSOR_BLINK_PERIOD, me, me._flashCharElement); - me._flashCharTimer.start(); + me._flashChar = 0; }, - unhighlightCharElement : func() { - if (me._flashCharTimer != nil) me._flashCharTimer.stop(); - me._flashCharTimer = nil; + _unhighlightCharElement : func() { me._highlightCharEnabled = 0; - me._highlightChar = 0; - me._flashCharElement(); + me._dataEntrySymbol[me._dataEntryPos].setDrawMode(canvas.Text.TEXT); + me._dataEntrySymbol[me._dataEntryPos].setColor(me._style.NORMAL_TEXT_COLOR); }, + isEditable : func () { return 1; }, isInEdit : func() { return (me._dataEntryPos != -1); }, + isHighlighted : func() { return me._highlightEnabled; }, enterElement : func() { # Handle pressing enter to confirm this element. @@ -179,7 +161,7 @@ var DataEntryElement = } # Highlight the first character element to indicate we're editing it - me.highlightCharElement(); + me._highlightCharElement(); } else { var charSym = me._dataEntrySymbol[me._dataEntryPos]; var incr_or_decr = (value > 0) ? 1 : -1; @@ -211,8 +193,8 @@ var DataEntryElement = if ((me._dataEntryPos == 0) and (incr_or_decr == -1)) return; # Don't scroll off the start if ((me._dataEntryPos == me._size -1) and (incr_or_decr == 1)) return; # Don't scroll off the end - me.unhighlightCharElement(); + me._unhighlightCharElement(); me._dataEntryPos = me._dataEntryPos + incr_or_decr; - me.highlightCharElement(); + me._highlightCharElement(); }, }; diff --git a/Nasal/canvas/PFD/HighlightElement.nas b/Nasal/canvas/PFD/HighlightElement.nas index 7bd278f67..6a62ccba3 100644 --- a/Nasal/canvas/PFD/HighlightElement.nas +++ b/Nasal/canvas/PFD/HighlightElement.nas @@ -33,36 +33,31 @@ var HighlightElement = setVisible : func(vis) { me._symbol.setVisible(vis); }, _flashElement : func() { - if (me._highlightEnabled == 0) { + if (me._highlighted == 0) { + me._symbol.setVisible(1); + me._highlighted = 1; + } else { me._symbol.setVisible(0); me._highlighted = 0; - } else { - if (me._highlighted == 0) { - me._symbol.setVisible(1); - me._highlighted = 1; - } else { - me._symbol.setVisible(0); - me._highlighted = 0; - } } }, highlightElement : func() { me._highlightEnabled = 1; me._highlighted = 0; - me._flashElement(); - me._flashTimer = maketimer(me._style.CURSOR_BLINK_PERIOD, me, me._flashElement); - me._flashTimer.start(); + # Force it to immediately display, rather than waiting for the timer + me._symbol.setVisible(1); + PFD.HighlightTimer.startHighlight(me, -1); }, unhighlightElement : func() { - if (me._flashTimer != nil) me._flashTimer.stop(); - me._flashTimer = nil; - me._highlightEnabled = 0; + me._symbol.setVisible(0); me._highlighted = 0; - me._flashElement(); + me._highlightEnabled = 0; + PFD.HighlightTimer.stopHighlight(me); }, isEditable : func () { return 0; }, isInEdit : func() { return 0; }, enterElement : func() { return me.getValue(); }, + isHighlighted : func() { return me._highlighted; }, clearElement : func() { }, editElement : func() { }, incrSmall : func(value) { }, diff --git a/Nasal/canvas/PFD/HighlightTimer.nas b/Nasal/canvas/PFD/HighlightTimer.nas new file mode 100644 index 000000000..1634df7e1 --- /dev/null +++ b/Nasal/canvas/PFD/HighlightTimer.nas @@ -0,0 +1,46 @@ +# Timer used for highlight UI elements + +var HighlightTimer = { + _elementList : {}, + _highlightTimer : nil, + _timerPeriod : 0.5, + + # Start highlighting an element for period time. Use -1 period argument to + # highlight until explicitly stopped by a call to stopHighlight. + startHighlight : func(element, period) { + me._elementList[element.getName()] = { Element: element, FlashCount : int(period / me._timerPeriod) }; + + if (me._highlightTimer == nil) { + me._highlightTimer = maketimer(me._timerPeriod, me, me.flashElements); + me._highlightTimer.singleShot = 0; + } + + if (me._highlightTimer.isRunning == 0) { + me._highlightTimer.restart(me._timerPeriod); + } + }, + + stopHighlight : func(element) { + # Set the flashcount to 0 so that it is unhighlighted below. + if (me._elementList[element.getName()] != nil) me._elementList[element.getName()].FlashCount = 0; + #delete(me._elementList, element.getName()); + }, + + flashElements : func() { + foreach (var element_name; keys(me._elementList)) { + if (me._elementList[element_name] == nil) continue; + var element = me._elementList[element_name].Element; + var flashCount = me._elementList[element_name].FlashCount; + + if (flashCount == 0) { + # Calling unhighlightElement will also stop the timer, as it calls stopHighlight, above + if (element.isHighlighted() == 1) element.unhighlightElement(); + } else { + me._elementList[element_name].FlashCount = flashCount - 1; + element._flashElement(); + } + } + + if (size(me._elementList) == 0) me._highlightTimer.stop(); + }, +}; diff --git a/Nasal/canvas/PFD/TextElement.nas b/Nasal/canvas/PFD/TextElement.nas index 85ecf3b73..2c2dd8859 100644 --- a/Nasal/canvas/PFD/TextElement.nas +++ b/Nasal/canvas/PFD/TextElement.nas @@ -17,53 +17,52 @@ var TextElement = obj.setValue(value); # State and timer for flashing highlighting of elements - # We need a separate Enabled flag as the timers are in a separate thread. - obj._highlightEnabled = 0; obj._highlighted = 0; - obj._flashTimer = nil; + obj._flash = 0; + + # Text to assign at the end of the highlight period. + # Used for annunicators that should flash and then change value. + obj._endText = nil; return obj; }, getName : func() { return me._name; }, - getValue : func() { return me._symbol.getText(); }, + getValue : func() { + if (me._symbol.getText() == nil) return ""; # Special case - canvas text elements return nil instead of empty string + return me._symbol.getText(); + }, setValue : func(value) { me._symbol.setText(value); }, setVisible : func(vis) { me._symbol.setVisible(vis); }, _flashElement : func() { - if (me._highlightEnabled == 0) { + if (me._flash == 0) { + me._symbol.setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX); + me._symbol.setColorFill(me._style.HIGHLIGHT_COLOR); + me._symbol.setColor(me._style.HIGHLIGHT_TEXT_COLOR); + me._flash = 1; + } else { me._symbol.setDrawMode(canvas.Text.TEXT); me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); - me._highlighted = 0; - } else { - if (me._highlighted == 0) { - me._symbol.setDrawMode(canvas.Text.TEXT + canvas.Text.FILLEDBOUNDINGBOX); - me._symbol.setColorFill(me._style.HIGHLIGHT_COLOR); - me._symbol.setColor(me._style.HIGHLIGHT_TEXT_COLOR); - me._highlighted = 1; - } else { - me._symbol.setDrawMode(canvas.Text.TEXT); - me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); - me._highlighted = 0; - } + me._flash = 0; } }, - highlightElement : func() { - me._highlightEnabled = 1; - me._highlighted = 0; - me._flashElement(); - me._flashTimer = maketimer(me._style.CURSOR_BLINK_PERIOD, me, me._flashElement); - me._flashTimer.start(); + highlightElement : func(highlighttime=-1, endText=nil) { + me._endText = endText; + me._highlighted = 1; + me._flash == 0; + PFD.HighlightTimer.startHighlight(me, highlighttime); }, unhighlightElement : func() { - if (me._flashTimer != nil) me._flashTimer.stop(); - me._flashTimer = nil; - me._highlightEnabled = 0; + if (me._endText != nil) me.setValue(me._endText); + me._endText = nil; me._highlighted = 0; - me._flashElement(); + me._symbol.setDrawMode(canvas.Text.TEXT); + me._symbol.setColor(me._style.NORMAL_TEXT_COLOR); + PFD.HighlightTimer.stopHighlight(me); }, isEditable : func () { return 0; }, isInEdit : func() { return 0; }, - isHighlighted : func() { return me._highlightEnabled; }, + isHighlighted : func() { return me._highlighted; }, enterElement : func() { return me.getValue(); }, clearElement : func() { }, editElement : func() { },