Skip to content

Instantly share code, notes, and snippets.

@SofieBrink
Last active August 4, 2024 16:06
Show Gist options
  • Save SofieBrink/5829fff6c04a2dd9de37b93a5b46f292 to your computer and use it in GitHub Desktop.
Save SofieBrink/5829fff6c04a2dd9de37b93a5b46f292 to your computer and use it in GitHub Desktop.
// !runscript XXXXXX
// Sofie's Universal Display Bootfile Version
//-------------------------------------------------------------------------------------------------
// Usage Description. Parameters with {OPT} infront can be left out.
// FormatTime(input) - Function to format a time in seconds to a 24 hour human-readable clock format
//-------------------------------------------------------------------------------------------------
// Variables
global UD_Widgets is lexicon().
set UD_Path to "1:/UniversalDisplay/".
set UD_Active to true.
set UD_Reboot to false.
set UD_HelperIndex to 0.
set UD_RefreshRate to 20.
set UD_HideOnInactive to true.
set UD_WidgetIndex to 0.
set EmptyList to ship:partsnamed("THIS PART WILL NEVER EXIST, I JUST NEED AN EMPTY LIST").
EmptyList:clear().
// datastructure:
// UD_Widgets
// lex {
// key: 0, value:
// lex {
// key: x, value: 200
// key: y, value: 200
// key: width, value: 250
// key: background, value: ""
// key: displayname, value: "Left Widget"
// key: fontsize, value: 16
// key: gui, value:
// lex {
// key: gui, value: guiElement
// key: contents, value:
// list {
// lex {
// key: layout, value: layoutElement
// key: contents, value:
// list {
// labelElement,
// labelElement
// }
// },
// lex {
// key: layout, value layoutElement
// key: contents, value:
// list {
// someElement
// }
// },
// lex {
// key: layout, value: layoutElement
// key: contents, value:
// list {
// labelElement,
// labelElement
// }
// },
// lex {
// key: layout, value layoutElement
// key: contents, value:
// list {
// someElement
// }
// },
// lex {
// key: layout, value: layoutElement
// key: contents, value:
// list {
// labelElement,
// labelElement
// }
// }
// }
// }
// key: contents, value:
// list {
// lex {
// key: type, value: "item"
// key: label, value: "Vessel"
// key: prefix, value: "",
// key: postfix, value: "",
// key: decimaldigits, value: 0,
// key: value, value: {return ship:name.}
// },
// lex {
// key: type, value: "seperator"
// },
// lex {
// key: type, value: "item"
// key: label, value: "Apoapsis"
// key: prefix, value: "",
// key: postfix, value: "Km",
// key: decimaldigits, value: 2,
// key: value, value: {return ship:apoapsis.}
// },
// lex {
// key: type, value: "spacer"
// },
// lex {
// key: type, value: "item"
// key: label, value: "Periapsis"
// key: prefix, value: "",
// key: postfix, value: "Km",
// key: decimaldigits, value: 2,
// key: value, value: {return ship:periapsis.}
// key: stringvalue, value: "{return ship:periapsis.}"
// }
// }
// }
// }
// TODO
// Colours low prio
// widget printer
// edit lines high prio
// documentation.
// Bugs
// spacers and seperators are not implemented
//-------------------------------------------------------------------------------------------------
// Helper Functions
global function FormatTime {
// Function to format a time in seconds to a 24 hour human-readable clock format
parameter input.
if (not input:typename = "scalar") return.
local result is choose "-" if input < 0 else "".
set input to abs(input).
local days is floor(input / (3600 * 24)).
local hours is floor(mod(input / 3600, 24)).
local minutes is floor((mod(input, 3600)) / 60).
local seconds is round(mod(input, 60), 1).
if (days <> 0) {
set result to result + days + "d ".
}
if (hours <> 0 OR days <> 0) {
set result to result + hours + "h ".
}
if ((minutes <> 0 OR hours <> 0) AND days = 0) {
set result to result + minutes + "m ".
}
if (hours = 0 AND days = 0 AND minutes <> 0) {
set result to result + round(seconds) + "s".
}
else if (hours = 0 AND days = 0 AND minutes = 0) {
set result to result + (seconds + ".0"):substring(0, choose 3 if seconds < 10 else 4) + "s".
}
return result.
}
//-------------------------------------------------------------------------------------------------
// Internal Helper Functions
function UD_Print {
// Function to allow printing of messages to the HUD for display with a closed terminal.
parameter message.
parameter type is "Info".
parameter short is true.
//print (choose "UD " if (short) else "Universal Display ") + type + ": " + message.
HUDTEXT((choose "UD " if (short) else "Universal Display ") + type + ": " + message
, 15 // Show for 5 seconds
, 2 // Upper right hand corner
, 24 // Font Size
, WHITE // Print in white
, true // Also print to the terminal for good measure
).
}
function UD_TestErrors {
// Function to test if there's any errors present and to print them.
parameter listing, type is "Error".
if (listing:length > 0) {
for li in listing {
UD_Print(li, type).
}
return false.
}
return true.
}
function UD_ValidateWidget {
// Function to test if a given widgetLex is valid.
parameter widget.
local errors is EmptyList:copy.
if (not(widget:haskey("DisplayName"))) errors:add("Item:DisplayName is empty").
else if (widget["DisplayName"]:typename <> "string") errors:add("Item:DisplayName is not a string.").
if (not(widget:haskey("Background"))) set widget["Background"] to "".
else if (widget["Background"]:typename <> "string") errors:add("Item:Background is not a string.").
if (not(widget:haskey("Width"))) set widget["Width"] to 250.
else if (widget["Width"]:typename <> "scalar") errors:add("Item:Width is not a scalar.").
if (not(widget:haskey("X"))) set widget["X"] to -60.
else if (widget["X"]:typename <> "scalar") errors:add("Item:X is not a scalar.").
if (not(widget:haskey("Y"))) set widget["Y"] to 550.
else if (widget["Y"]:typename <> "scalar") errors:add("Item:Y is not a scalar.").
if (not(widget:haskey("FontSize"))) set widget["FontSize"] to 16.
else if (widget["FontSize"]:typename <> "scalar") errors:add("Item:FontSize is not a scalar.").
if (widget:haskey("Contents") AND widget["Contents"]:typename <> "list") errors:add("Item:Contents is not a list.").
return lex("widget", widget, "errors", errors).
}
function UD_ValidateLine {
// Function to test if a given lineLex is valid.
parameter line.
local errors is EmptyList:copy.
if (not(line:haskey("Type"))) set line["Type"] to "item".
else if (line["Type"]:typename <> "string") errors:add("Item:Type is not a string.").
if (line["Type"] = "item") {
if (not(line:haskey("Label"))) errors:add("Item:Label is empty").
else if (line["Label"]:typename <> "string") errors:add("Item:Label is not a string.").
if (not(line:haskey("Prefix"))) set line["Prefix"] to "".
else if (line["Prefix"]:typename <> "string") errors:add("Item:Prefix is not a string.").
if (not(line:haskey("Postfix"))) set line["Postfix"] to "".
else if (line["Postfix"]:typename <> "string") errors:add("Item:Postfix is not a string.").
if (not(line:haskey("DecimalDigits"))) set line["DecimalDigits"] to 0.
else if (line["DecimalDigits"]:typename <> "scalar") errors:add("Item:DecimalDigits is not a scalar.").
// Validate delegate as much as possible.
if (line:haskey("Value")
AND line["Value"]:typename = "string"
AND line["Value"]:length >= 2
AND line["Value"][0] = "{"
AND line["Value"][line["Value"]:length - 1] = "}"
) {
// Split delegate into components.
local knownDirectives is EmptyList:copy.
knownDirectives:add("set").
knownDirectives:add("lock").
knownDirectives:add("local").
knownDirectives:add("return").
local statements is EmptyList:copy.
statements:add("").
local inString is false.
for char in line["Value"]:substring(1, line["Value"]:length - 2) {
set statements[statements:length-1] to statements[statements:length-1] + char.
if (char = char(34)) toggle inString.
if (not(inString) AND char = char(46)) {
set statements[statements:length-1] to statements[statements:length-1]:trim().
statements:add("").
}
}
if (statements[statements:length-1]:length = 0)
statements:remove(statements:length-1).
for statement in statements {
if (not(statement:endswith(".")))
errors:add("Item:Value does not end with a period").
if (statement:contains("{") OR statement:contains("}")) {
errors:add("Item:Value statement contains a nested delegate").
// Nested Delegates are currently unsupported
}
if not(knownDirectives:contains(statement:split(" ")[0])) {
errors:add("Item:Value statement has an unknown or disallowed directive.").
}
}
if (not(statements[statements:length-1]:startswith("return "))) {
errors:add("Item:Value does not end with a return statement.").
}
}
else {
if (line:haskey("Value")) errors:add("Item:Value is not a stringified delegate.").
else errors:add("Item:Value is empty.").
}
}
else if (line["Type"] <> "spacer" OR line["Type"] <> "seperator") errors:add("Item:Type " + line["Type"] + " is unkown.").
return lex("line", line, "errors", errors).
}
function UD_FindWidget {
// Function to find the widget that matches the provided index or display name, returns false if no or multiple matches exist.
parameter dub.
local matchingItems is EmptyList:copy.
for w in UD_Widgets:keys {
if (dub:typename = "scalar" AND w = dub) {
matchingItems:add(w).
}
else if (dub:typename = "string" and UD_Widgets[w]["DisplayName"] = dub) {
matchingItems:add(w).
}
}
if (matchingItems:length = 1) {
return matchingItems[0].
}
else {
return false.
}
}
function UD_RunHelper {
// Function to write to and run the helper file.
parameter command.
log command to UD_Path + "UniversalDisplayHelper" + UD_HelperIndex + ".ks".
runpath(UD_Path + "UniversalDisplayHelper" + UD_HelperIndex + ".ks").
deletePath(UD_Path + "UniversalDisplayHelper" + UD_HelperIndex + ".ks").
set UD_HelperIndex to UD_HelperIndex + 1.
}
function UD_DeleteData {
// Function to help remove data from the saved file and the active list.
parameter widgetIndex, lineIndexes is EmptyList:copy, deleteWidget is false.
local widgetDub is "UD_Widgets:add(" + widgetIndex + ", ".
local lineDub is "UD_Widgets[" + widgetIndex + "][" + char(34) + "Contents" + char(34) + "]:add(".
local file is open(UD_Path + "UniversalDisplayValues.ks").
local fileContents is file:readall().
local index is 0.
file:clear().
for line in fileContents {
if (line:contains(widgetDub) AND deleteWidget) {
// don't save.
}
else if (line:contains(lineDub)) {
if (deleteWidget OR lineIndexes:contains(index)) {
// don't save.
}
else {
file:writeln(line).
}
set index to index + 1.
}
else {
file:writeln(line).
}
}
if (deleteWidget) {
UD_Widgets[widgetIndex]["GUI"]["GUI"]:dispose().
UD_Widgets:remove(widgetIndex).
}
else {
for i in lineIndexes {
UD_Widgets[widgetIndex]["GUI"]["Contents"][i]["Layout"]:dispose().
UD_Widgets[widgetIndex]["GUI"]["Contents"]:remove(i).
UD_Widgets[widgetIndex]["Contents"]:remove(i).
}
}
}
function UD_EditData {
// function to edit one line of the saved file
parameter dub, newline.
local file is open(UD_Path + "UniversalDisplayValues.ks").
local fileContents is file:readall().
local index is 0.
file:clear().
for line in fileContents {
if (line:contains(dub)) {
file:writeln(newline).
}
else {
file:writeln(line).
}
}
}
function UD_ConstructWidgetCommand {
// function to make a command to save the widget.
parameter index, commandLex.
local command is "".
set command to "UD_Widgets:add(" + index + ", lex(".
set command to command + char(34) + "DisplayName" + char(34) + ", " + char(34) + commandLex["DisplayName"] + char(34) + ", ".
set command to command + char(34) + "Background" + char(34) + ", " + char(34) + commandLex["Background"] + char(34) + ", ".
set command to command + char(34) + "Width" + char(34) + ", " + commandLex["Width"] + ", ".
set command to command + char(34) + "X" + char(34) + ", " + commandLex["X"] + ", ".
set command to command + char(34) + "Y" + char(34) + ", " + commandLex["Y"] + ", ".
set command to command + char(34) + "FontSize" + char(34) + ", " + commandLex["FontSize"] + ", ".
set command to command + char(34) + "Contents" + char(34) + ", " + "list()".
set command to command + ")).".
return command.
}
//-------------------------------------------------------------------------------------------------
// Internal Functions
function UD_Loop {
// Function to start the internal loop to that will call functions to continually update the widgets.
on floor(time:seconds * UD_RefreshRate) {
UD_HandleMessages().
UD_UpdateWidgets().
return UD_Active.
}
wait until (not(UD_Active)).
UD_Save().
clearGuis().
if (UD_Reboot) reboot.
}
function UD_Save {
// Function to save the settings to a json file for loading on the next reboot.
local settingsLex is lexicon(
"UD_WidgetIndex", UD_WidgetIndex,
"UD_RefreshRate", UD_RefreshRate,
"UD_HideOnInactive", UD_HideOnInactive
).
writeJson(settingsLex, UD_Path + "UniversalDisplaySettings.json").
}
function UD_Load {
// Function to load the settings from the json file, validate the exit state and load the widgets from storage.
if (homeConnection:isconnected) {
if (core:bootfilename = "boot/UniversalDisplay.ks" AND exists("0:/UniversalDisplay.ks")) copypath("0:/UniversalDisplay.ks", "1:/boot/UniversalDisplay.ks").
else if (exists("0:/UniversalDisplay.ksm")) {
copypath("0:/UniversalDisplay.ksm", "1:/boot/UniversalDisplay.ksm").
if (not(core:bootfilename = "boot/UniversalDisplay.ksm")) set core:bootfilename to "boot/UniversalDisplay.ksm".
}
else {
UD_Print("Archive is accessible but source files were not found!", "Warning").
}
}
if (exists(UD_Path)) {
cd(UD_Path).
list files in fileList.
for file in fileList {
if file:name:contains("UniversalDisplayHelper") and file:extension = "ks" {
UD_Print("Helper file detected indicating a possible syntax error on previous run!", "Warning").
deletepath(UD_Path + file:name).
}
else if (file:name = "UniversalDisplaySettings.json") {
local settingsLex is readJson(UD_Path + "UniversalDisplaySettings.json").
if (settingsLex:haskey("UD_WidgetIndex")) set UD_WidgetIndex to settingsLex["UD_WidgetIndex"].
if (settingsLex:haskey("UD_RefreshRate")) set UD_RefreshRate to settingsLex["UD_RefreshRate"].
if (settingsLex:haskey("UD_HideOnInactive")) set UD_HideOnInactive to settingsLex["UD_HideOnInactive"].
UD_Print("Loaded settings from disk").
}
else if (file:name = "UniversalDisplayValues.ks") {
runPath(UD_Path + "UniversalDisplayValues.ks").
}
else if (file:name = "UniversalDisplayValues_Backup.ks") {
UD_Print("Backup file found without main file. Recovering from backup.").
runPath(UD_Path + "UniversalDisplayValues_Backup.ks").
}
}
cd("1:/").
}
}
function UD_Backup {
// Function to create a backup of the current UniversalDisplayValues, incase something goes terribly wrong.
if (exists(UD_Path + "UniversalDisplayValues.ks")) {
copyPath(UD_Path + "UniversalDisplayValues.ks", UD_Path + "UniversalDisplayValues_Backup.ks").
}
}
function UD_HandleMessages {
// Function to handle incomming messages from other cores to update various things.
if (core:messages:empty) return.
local msg is core:messages:pop.
local errors is EmptyList:copy.
UD_Backup().
if (msg:content:typename = "string") {
if (msg:content = "exit") {
UD_Print("Exiting").
UD_Active off.
}
else if (msg:content = "reboot") {
UD_Print("Rebooting").
UD_Reboot on.
UD_Active off.
}
else if (msg:content = "refresh") {
UD_Print("Refresing").
for key in UD_Widgets:keys {
UD_Widgets[key]["GUI"]["GUI"]:dispose().
UD_Widgets[key]:remove("GUI").
}
}
else if (msg:content = "show") {
core:doevent("open terminal").
}
else if (msg:content = "hide") {
core:doevent("close terminal").
}
else if (msg:content = "reset") {
UD_Print("Resetting").
if (exists(UD_Path + "UniversalDisplaySettings.json")) deletePath(UD_Path + "UniversalDisplaySettings.json").
if (exists(UD_Path + "UniversalDisplayValues.ks")) deletePath(UD_Path + "UniversalDisplayValues.ks").
clearguis().
reboot.
}
}
else if (msg:content:typename = "lexicon") {
local receivedLex is msg:content.
if (receivedLex:haskey("RefreshRate")) {
if (receivedLex["RefreshRate"]:typename = "scalar") {
set UD_RefreshRate to receivedLex["RefreshRate"].
UD_Print("Updating RefreshRate to " + UD_RefreshRate).
}
else {
UD_Print("Received RefreshRate is not a scalar.", "Warning").
}
}
if (receivedLex:haskey("HideOnInactive")) {
if (receivedLex["HideOnInactive"]:typename = "boolean") {
set UD_HideOnInactive to receivedLex["HideOnInactive"].
UD_Print("Updating HideOnInactive to " + UD_RefreshRate).
}
else {
UD_Print("Received HideOnInactive is not a boolean.", "Warning").
}
}
if (receivedLex:haskey("AddWidget")) {
if (receivedLex["AddWidget"]:typename = "lexicon") {
UD_AddWidget(receivedLex["AddWidget"]).
}
else {
UD_Print("Received AddWidget is not a lexicon.", "Warning").
}
}
else if (receivedLex:haskey("RemoveWidget")) {
if (receivedLex["RemoveWidget"]:typename = "scalar" OR receivedLex["RemoveWidget"]:typename = "string") {
UD_Print("Removing widget").
UD_RemoveWidget(receivedLex["RemoveWidget"]).
}
else {
UD_Print("Received RemoveWidget is not a string or scalar.", "Warning").
}
}
else if (receivedLex:haskey("EditWidget")) {
if (receivedLex["EditWidget"]:typename = "lexicon") {
UD_Print("Editing widget").
UD_EditWidget(receivedLex["EditWidget"]).
}
else {
UD_Print("Received EditWidget is not a lexicon.", "Warning").
}
}
else if (receivedLex:haskey("AddLine")) {
if (receivedLex["AddLine"]:typename = "lexicon") {
if (not(receivedLex["AddLine"]:haskey("Widget"))) errors:add("Item:Widget is empty").
else if (not(receivedLex["AddLine"]["Widget"]:typename = "string" OR receivedLex["AddLine"]["Widget"]:typename = "scalar")) errors:add("Item:Widget is not a string or scalar.").
if (not(receivedLex["AddLine"]:haskey("Line"))) errors:add("Item:Line is empty").
else if (receivedLex["AddLine"]["Line"]:typename <> "lexicon") errors:add("Item:Line is not a lexicon.").
if (not(UD_TestErrors(errors))) return false.
if (not(receivedLex["AddLine"]:haskey("Index"))) set receivedLex["AddLine"]["Index"] to -1.
else if (receivedLex["AddLine"]["Index"]:typename <> "scalar") errors:add("Item:Index is not a Scalar.").
local widgetIndex is UD_FindWidget(receivedLex["AddLine"]["Widget"]).
if (widgetIndex:typename <> "scalar" AND receivedLex["AddLine"]["Widget"]:typename = "scalar") errors:add("Item:Widget at index " + receivedLex["AddLine"]["Widget"] + " does not exist.").
else if (widgetIndex:typename <> "scalar") errors:add("Item:Widget with DisplayName " + receivedLex["AddLine"]["Widget"] + " does not exist, or there are multiple.").
if (not(UD_TestErrors(errors))) return false.
set receivedLex["AddLine"]["Index"] to choose receivedLex["AddLine"]["Index"] if UD_Widgets[widgetIndex]["Contents"]:length <> receivedLex["AddLine"]["Index"] else -1.
if (receivedLex["AddLine"]["Index"] <> -1) {
UD_Widgets[widgetIndex]["GUI"]["GUI"]:dispose().
UD_Widgets[widgetIndex]:remove("GUI").
}
local line is UD_AddLine(widgetIndex, receivedLex["AddLine"]["Line"], receivedLex["AddLine"]["Index"]).
if (line:typename <> "lexicon") {
errors:add("Failed to add line to widget").
}
else {
if (line:haskey("command") AND line:haskey("checksum")) {
UD_RunHelper(line["checksum"]).
UD_Print("Checksum passed.").
UD_RunHelper(line["command"]).
UD_Print("Command Executed.").
}
else errors:add("Internal Error when adding line").
}
if (not(UD_TestErrors(errors))) return false.
}
else {
UD_Print("Received AddWidget is not a lexicon.", "Warning").
}
}
else if (receivedLex:haskey("RemoveLine")) {
UD_Print("Removing line").
UD_RemoveLine(receivedLex["RemoveLine"]).
}
else if (receivedLex:haskey("EditLine")) {
UD_Print("Unsupported").
}
}
}
function UD_AddWidget {
// Function to add a widget to the list
parameter addLex.
local errors is EmptyList:copy.
local widget is lex().
local commands is EmptyList:copy.
commands:add("").
local checksums is EmptyList:copy.
if (addLex:typename <> "lexicon") errors:add("Item is not a lexicon.").
if (not(UD_TestErrors(errors))) return false.
local validation is UD_ValidateWidget(addLex).
for e in validation["errors"] errors:add(e).
set addLex to validation["widget"].
if (not(UD_TestErrors(errors))) return false.
set commands[0] to UD_ConstructWidgetCommand(UD_WidgetIndex, addLex).
widget:add("DisplayName", addLex["DisplayName"]).
widget:add("Background", addLex["Background"]).
widget:add("Width", addLex["Width"]).
widget:add("X", addLex["X"]).
widget:add("Y", addLex["Y"]).
widget:add("FontSize", addLex["FontSize"]).
widget:add("Contents", EmptyList:copy).
if (addLex:haskey("Contents")) {
for listItem in addLex["Contents"] {
local line is UD_addLine(UD_WidgetIndex, listItem).
if (line:typename <> "lexicon") {
errors:add("Failed to add line to widget").
}
else {
if (line:haskey("command")) commands:add(line["command"]). else errors:add("Internal Error when adding line").
if (line:haskey("checksum")) checksums:add(line["checksum"]). else errors:add("Internal Error when adding line").
}
}
}
if (not(UD_TestErrors(errors))) return false.
log commands[0] to UD_Path + "UniversalDisplayValues.ks".
UD_Widgets:add(UD_WidgetIndex, widget).
if (checksums:length > 0) {
UD_RunHelper(checksums:join(" ")).
UD_Print(checksums:length + " Checksums passed.").
}
if (commands:length >= 2) {
local tmpcommand is "".
for i in range(1, commands:length) {
set tmpcommand to tmpcommand + commands[i] + " ".
}
UD_RunHelper(tmpcommand).
UD_Print("Commands Executed.").
}
set UD_WidgetIndex to UD_WidgetIndex + 1.
return true.
}
function UD_RemoveWidget {
// Function to remove a widget from the list.
parameter removeIndex.
local errors is EmptyList:copy.
local widgetIndex is UD_FindWidget(removeIndex).
if (widgetIndex:typename <> "scalar" AND removeIndex:typename = "scalar") errors:add("Item:Widget at index " + removeIndex + " does not exist.").
else if (widgetIndex:typename <> "scalar") errors:add("Item:Widget with DisplayName " + removeIndex + " does not exist, or there are multiple.").
if (not(UD_TestErrors(errors))) return false.
UD_DeleteData(widgetIndex, EmptyList:copy, true).
}
function UD_EditWidget {
// Function to edit an existing widget.
parameter editLex.
local errors is EmptyList:copy.
local replacementLex is lex().
local command is "".
if (not(editLex:haskey("Index"))) errors:add("Item:Index is empty").
else if (editLex["Index"]:typename <> "scalar" AND editLex["Index"]:typename <> "string") errors:add("Item:Index is not a string or scalar.").
if (not(editLex:haskey("Widget"))) errors:add("Item:Widget is empty").
else if (editLex["Widget"]:typename <> "lexicon") errors:add("Item:Widget is not a lexicon.").
if (not(UD_TestErrors(errors))) return false.
local widgetIndex is UD_FindWidget(editLex["Index"]).
if (widgetIndex:typename <> "scalar" AND editLex["Widget"]:typename = "scalar") errors:add("Item:Widget at index " + editLex["Widget"] + " does not exist.").
else if (widgetIndex:typename <> "scalar") errors:add("Item:Widget with DisplayName " + editLex["Widget"] + " does not exist, or there are multiple.").
if (not(UD_TestErrors(errors))) return false.
if (editLex["Widget"]:haskey("DisplayName")) {
if (editLex["Widget"]["DisplayName"]:typename <> "string") errors:add("Item:DisplayName is not a string.").
set replacementLex["DisplayName"] to editLex["Widget"]["DisplayName"].
}
else {
set replacementLex["DisplayName"] to UD_Widgets[widgetIndex]["DisplayName"].
}
if (editLex["Widget"]:haskey("Background")) {
if (editLex["Widget"]["Background"]:typename <> "string") errors:add("Item:Background is not a string.").
set replacementLex["Background"] to editLex["Widget"]["Background"].
}
else {
set replacementLex["Background"] to UD_Widgets[widgetIndex]["Background"].
}
if (editLex["Widget"]:haskey("Width")) {
if (editLex["Widget"]["Width"]:typename <> "scalar") errors:add("Item:Width is not a string.").
set replacementLex["Width"] to editLex["Widget"]["Width"].
}
else {
set replacementLex["Width"] to UD_Widgets[widgetIndex]["Width"].
}
if (editLex["Widget"]:haskey("X")) {
if (editLex["Widget"]["X"]:typename <> "scalar") errors:add("Item:X is not a string.").
set replacementLex["X"] to editLex["Widget"]["X"].
}
else {
set replacementLex["X"] to UD_Widgets[widgetIndex]["X"].
}
if (editLex["Widget"]:haskey("Y")) {
if (editLex["Widget"]["Y"]:typename <> "scalar") errors:add("Item:Y is not a string.").
set replacementLex["Y"] to editLex["Widget"]["Y"].
}
else {
set replacementLex["Y"] to UD_Widgets[widgetIndex]["Y"].
}
if (editLex["Widget"]:haskey("FontSize")) {
if (editLex["Widget"]["FontSize"]:typename <> "scalar") errors:add("Item:FontSize is not a string.").
set replacementLex["FontSize"] to editLex["Widget"]["FontSize"].
}
else {
set replacementLex["FontSize"] to UD_Widgets[widgetIndex]["FontSize"].
}
if (not(UD_TestErrors(errors))) return false.
local isDifferent is false.
for key in replacementLex:keys {
if (not(replacementLex[key] = UD_Widgets[widgetIndex][key])) isDifferent on.
}
if (not(isDifferent)) errors:add("New widget is not different from existing widget").
if (not(UD_TestErrors(errors))) return false.
local validation is UD_ValidateWidget(replacementLex).
for e in validation["errors"] errors:add(e).
set replacementLex to validation["widget"].
if (not(UD_TestErrors(errors))) return false.
set command to UD_ConstructWidgetCommand(widgetIndex, replacementLex).
UD_EditData("UD_Widgets:add(" + widgetIndex + ", ", command).
for key in replacementLex:keys {
set UD_Widgets[widgetIndex][key] to replacementLex[key].
}
}
function UD_AddLine {
// function to add a line to a widget.
parameter widgetIndex, addLex, lineIndex is -1.
local errors is EmptyList:copy.
local preCommand is "".
local lineLex is "".
local command is "".
local checksum is "".
if (widgetIndex:typename <> "scalar") errors:add("WidgetIndex is not a scalar.").
if (addLex:typename <> "lexicon") errors:add("Item is not a lexicon.").
if (lineIndex:typename <> "scalar") errors:add("LineIndex is not a scalar.").
local validation is UD_ValidateLine(addLex).
for e in validation["errors"] errors:add(e).
set addLex to validation["line"].
if (not(UD_TestErrors(errors))) return false.
set preCommand to "UD_Widgets[" + widgetIndex + "][" + char(34) + "Contents" + char(34) + "]".
if (addLex["Type"] = "item") {
set checksum to "(" + addLex["value"] + "):call().".
set lineLex to "lex(".
set lineLex to lineLex + char(34) + "Type" + char(34) + ", " + char(34) + addLex["Type"] + char(34) + ", ".
set lineLex to lineLex + char(34) + "Label" + char(34) + ", " + char(34) + addLex["Label"] + char(34) + ", ".
set lineLex to lineLex + char(34) + "Prefix" + char(34) + ", " + char(34) + addLex["Prefix"] + char(34) + ", ".
set lineLex to lineLex + char(34) + "Postfix" + char(34) + ", " + char(34) + addLex["Postfix"] + char(34) + ", ".
set lineLex to lineLex + char(34) + "DecimalDigits" + char(34) + ", " + addLex["DecimalDigits"] + ", ".
set lineLex to lineLex + char(34) + "Value" + char(34) + ", " + addLex["Value"] + ", ".
set lineLex to lineLex + char(34) + "StringValue" + char(34) + ", " + char(34) + addLex["Value"] + char(34).
set lineLex to lineLex + ")".
}
else {
set lineLex to "lex(" + char(34) + "Type" + char(34) + ", " + char(34) + addLex["Type"] + char(34) + ")".
}
set command to "if (not(exists(" + char(34) + UD_Path + "UniversalDisplayValues.ks" + char(34) + "))) create(" + char(34) + UD_Path + "UniversalDisplayValues.ks" + char(34) + ").".
set command to command + "local index is 0. local file is open(" + char(34) + UD_Path + "UniversalDisplayValues.ks" + char(34) + "). local fileContents is file:readall().".
set command to command + "set addLine to {file:writeln(" + char(34) + (preCommand + ":add(" + lineLex + ")."):replace(char(34), char(34) + " + char(34) + " + char(34)) + char(34) + ").}.".
set command to command + "if (fileContents:length = 0) addLine(). else {".
if (lineIndex <> -1) {
set command to command + "local lines is list().".
set command to command + "for line in fileContents { if line:contains(" + char(34) + "UD_Widgets[" + widgetIndex + "][" + char(34) + " + char(34) + " + char(34) + "Contents" + char(34) + " + char(34) +" + char(34) + "]:add(" + char(34) + ") {".
set command to command + "lines:add(list(line, index)). set index to index + 1.}else lines:add(list(line)).} file:clear().".
set command to command + "for i in range(lines:length) { if (lines[i]:length = 2 AND lines[i][1] = " + lineIndex + ") addLine(). file:writeln(lines[i][0]).} }".
}
else {
set command to command + " addLine(). }".
}
set command to command + preCommand + (choose ":insert(" + lineIndex if lineIndex <> -1 else ":add(") + lineLex + ").".
return lex("command", command, "checksum", checksum).
}
function UD_RemoveLine {
// Function to remove a line from a widget.
parameter removeLex.
local errors is EmptyList:copy.
if (not(removeLex:haskey("Widget"))) errors:add("Item:Widget is empty").
else if (not(removeLex["Widget"]:typename = "string" OR removeLex["Widget"]:typename = "scalar")) errors:add("Item:Widget is not a string or scalar.").
if (not(removeLex:haskey("Index"))) errors:add("Item:Index is empty").
else if (not(removeLex["Index"]:typename = "scalar")) errors:add("Item:Index is not a scalar.").
if (not(UD_TestErrors(errors))) return false.
local widgetIndex is UD_FindWidget(removeLex["Widget"]).
if (widgetIndex:typename <> "scalar" AND removeLex["Widget"]:typename = "scalar") errors:add("Item:Widget at index " + removeLex["Widget"] + " does not exist.").
else if (widgetIndex:typename <> "scalar") errors:add("Item:Widget with DisplayName " + removeLex["Widget"] + " does not exist, or there are multiple.").
if (not(UD_TestErrors(errors))) return false.
If (removeLex["Index"] < 0 OR removeLex["Index"] >= UD_Widgets[widgetIndex]["Contents"]:length) errors:add("Item:Index is out of range.").
if (not(UD_TestErrors(errors))) return false.
local tmplist is EmptyList:copy.
tmplist:add(removeLex["Index"]).
UD_DeleteData(widgetIndex, tmplist, false).
}
function UD_UpdateWidgets {
for key in UD_Widgets:keys {
local widget is UD_Widgets[key].
if (not(widget:haskey("GUI"))) set widget["GUI"] to lex().
if (not(widget["GUI"]:haskey("GUI"))) {
set widget["GUI"]["GUI"] to gui(widget["Width"]).
widget["GUI"]["GUI"]:show().
if widget["Background"] = "" set widget["GUI"]["GUI"]:Style:bg to "".
}
// Update the direct elements of the widget.
if (not(widget["GUI"]["GUI"]:Style:bg = widget["Background"])) set widget["GUI"]["GUI"]:Style:bg to widget["Background"].
if (not(widget["GUI"]["GUI"]:X = widget["X"])) set widget["GUI"]["GUI"]:X to widget["X"].
if (not(widget["GUI"]["GUI"]:Y = widget["Y"])) set widget["GUI"]["GUI"]:Y to widget["Y"].
if (not(widget["GUI"]["GUI"]:Style:Width = widget["Width"])) set widget["GUI"]["GUI"]:Style:Width to widget["Width"].
if (not(widget["GUI"]["GUI"]:Style:FontSize = widget["FontSize"])) set widget["GUI"]["GUI"]:Style:FontSize to widget["FontSize"].
if (not(widget["GUI"]["GUI"]:visible) and kuniverse:activevessel = ship) widget["GUI"]["GUI"]:show().
else if (widget["GUI"]["GUI"]:visible and UD_HideOnInactive and kuniverse:activevessel <> ship) widget["GUI"]["GUI"]:hide().
if (not(widget["GUI"]["GUI"]:visible)) return. // Don't need to tick the lines if the widget isn't visible.
local index is 0.
for line in widget["Contents"] {
if (not(widget["GUI"]:haskey("Contents"))) set widget["GUI"]["Contents"] to EmptyList:copy.
if (not(widget["GUI"]["Contents"]:length > index)) widget["GUI"]["Contents"]:add(lex()).
if (not(widget["GUI"]["Contents"][index]:haskey("Layout"))) set widget["GUI"]["Contents"][index]["Layout"] to widget["GUI"]["GUI"]:addhlayout().
if (not(widget["GUI"]["Contents"][index]:haskey("Contents"))) set widget["GUI"]["Contents"][index]["Contents"] to EmptyList:copy.
if (line["Type"] = "item") {
local labeltxt is "<color=white>" + line["Label"] + ":</color>".
local prefixtxt is "<color=white>" + line["Prefix"] + "</color>".
local postfixtxt is "<color=white> " + line["Postfix"] + "</color>".
local value is line["Value"]:call().
local valuetxt is prefixtxt + "<color=yellow>" + (choose round(value, line["DecimalDigits"]) if value:typename = "scalar" else value) + "</color>" + postfixtxt.
if (not(widget["GUI"]["Contents"][index]["Contents"]:length > 0)) widget["GUI"]["Contents"][index]["Contents"]:add(widget["GUI"]["Contents"][index]["Layout"]:addlabel()).
if (not(widget["GUI"]["Contents"][index]["Contents"]:length > 1)) widget["GUI"]["Contents"][index]["Contents"]:add(widget["GUI"]["Contents"][index]["Layout"]:addlabel()).
// specifics for the two labels in an item
if (not(widget["GUI"]["Contents"][index]["Contents"][0]:text = labeltxt)) set widget["GUI"]["Contents"][index]["Contents"][0]:text to labeltxt.
if (not(widget["GUI"]["Contents"][index]["Contents"][1]:text = valuetxt)) set widget["GUI"]["Contents"][index]["Contents"][1]:text to valuetxt.
if (not(widget["GUI"]["Contents"][index]["Contents"][1]:style:align = "RIGHT")) set widget["GUI"]["Contents"][index]["Contents"][1]:style:align to "RIGHT".
}
// basic markup for all labels
for label in widget["GUI"]["Contents"][index]["Contents"] {
if (not(label:style:margin:top = 0)) set label:style:margin:top to 0.
if (not(label:style:margin:bottom = 0)) set label:style:margin:bottom to 0.
if (not(label:style:margin:left = 0)) set label:style:margin:left to 0.
if (not(label:style:margin:right = 0)) set label:style:margin:right to 0.
if (not(label:style:padding:top = round(Widget["FontSize"] / 12, 1))) set label:style:padding:top to round(Widget["FontSize"] / 12, 1).
if (not(label:style:padding:bottom = round(Widget["FontSize"] / 12, 1))) set label:style:padding:bottom to round(Widget["FontSize"] / 12, 1).
if (not(label:style:padding:left = round(Widget["FontSize"] / 12, 1))) set label:style:padding:left to round(Widget["FontSize"] / 12, 1).
if (not(label:style:padding:right = round(Widget["FontSize"] / 12, 1))) set label:style:padding:right to round(Widget["FontSize"] / 12, 1).
if (not(label:style:fontsize = Widget["FontSize"] * 0.8125)) set label:style:fontsize to Widget["FontSize"] * 0.8125.
if (not(label:style:wordwrap = false)) set label:style:wordwrap to false.
}
set index to index + 1.
}
}
}
//-------------------------------------------------------------------------------------------------
// Function Calls Post-Loading
clearGuis().
UD_Load().
UD_Loop().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment