- offer 10 "tabs" (with more configurable) where each can contain Nasal code;
all code shares a common namespace, so variables defined in one tab can be used in another during the whole fgfs session - make code persistent (saved in autosave.xml) - better error messages (code borrowed from nasal/lib/interactive.nas) - clear button (replaces close button, which is now on the title bar) - help button (explains how to add more tab buttons, etc.)
This commit is contained in:
parent
6fdf07b79e
commit
879620694e
1 changed files with 230 additions and 62 deletions
|
@ -6,81 +6,249 @@
|
|||
<name>nasal-console</name>
|
||||
<layout>vbox</layout>
|
||||
|
||||
<text>
|
||||
<label>Nasal Console</label>
|
||||
</text>
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<text>
|
||||
<label>Nasal Console</label>
|
||||
</text>
|
||||
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<button>
|
||||
<pref-width>16</pref-width>
|
||||
<pref-height>16</pref-height>
|
||||
<legend></legend>
|
||||
<keynum>27</keynum>
|
||||
<border>2</border>
|
||||
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<textbox>
|
||||
<name>editfield</name>
|
||||
<halign>fill</halign>
|
||||
<pref-height>120</pref-height>
|
||||
<pref-width>450</pref-width>
|
||||
<pref-height>200</pref-height>
|
||||
<slider>20</slider>
|
||||
<editable>true</editable>
|
||||
<property>/sim/gui/dialogs/nasal-console/code</property>
|
||||
<property>/sim/gui/dialogs/nasal-console/edit</property>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
</textbox>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<default-padding>8</default-padding>
|
||||
<halign>fill</halign>
|
||||
<default-padding>4</default-padding>
|
||||
|
||||
<text>
|
||||
<label>TAB to leave edit field</label>
|
||||
<button-template>
|
||||
<hide>1</hide>
|
||||
<halign>fill</halign>
|
||||
<color>
|
||||
<red>0.5</red>
|
||||
<green>0.5</green>
|
||||
<blue>0.5</blue>
|
||||
</color>
|
||||
</text>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<default-padding>0</default-padding>
|
||||
|
||||
<button>
|
||||
<legend>Close</legend>
|
||||
<key>Esc</key>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-close</command>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Print</legend>
|
||||
<key>Ctrl-p</key>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>
|
||||
rule = "--------------------------------------------------";
|
||||
print(rule ~ "\n");
|
||||
print(getprop("/sim/gui/dialogs/nasal-console/code"));
|
||||
print(rule);
|
||||
</script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Execute</legend>
|
||||
<default>true</default>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>compile(getprop("/sim/gui/dialogs/nasal-console/code"))()</script>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
<pref-width>20</pref-width>
|
||||
<pref-height>20</pref-height>
|
||||
<one-shot>0</one-shot>
|
||||
<live>1</live>
|
||||
<legend>## 1 ##</legend>
|
||||
<property>## /sim/gui/dialogs/nasal-console/tab-down[1] ##</property>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>## select(1) ##</script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-update</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
</button-template>
|
||||
</group>
|
||||
|
||||
<hrule/>
|
||||
|
||||
<group>
|
||||
<layout>hbox</layout>
|
||||
<default-padding>4</default-padding>
|
||||
|
||||
<button>
|
||||
<legend>?</legend>
|
||||
<pref-width>30</pref-width>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>help()</script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-update</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<empty><stretch>1</stretch></empty>
|
||||
|
||||
<button>
|
||||
<legend>Clear</legend>
|
||||
<key>Ctrl-c</key>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>active and clear()</script>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>dialog-update</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Print</legend>
|
||||
<key>Ctrl-p</key>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>active and dump()</script>
|
||||
</binding>
|
||||
</button>
|
||||
|
||||
<button>
|
||||
<legend>Execute</legend>
|
||||
<default>true</default>
|
||||
<equal>true</equal>
|
||||
<binding>
|
||||
<command>dialog-apply</command>
|
||||
<object-name>editfield</object-name>
|
||||
</binding>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>active and execute()</script>
|
||||
</binding>
|
||||
</button>
|
||||
</group>
|
||||
|
||||
<nasal>
|
||||
<open>
|
||||
var dlg = props.globals.getNode("/sim/gui/dialogs/nasal-console");
|
||||
var kbdctrl = props.globals.getNode("/devices/status/keyboard/ctrl", 1);
|
||||
var printf = func { print(call(sprintf, arg)) }
|
||||
var edit = dlg.getNode("edit", 1);
|
||||
if (!contains(globals, "__nasal_console_locals")) {
|
||||
globals["__nasal_console_locals"] = {};
|
||||
}
|
||||
var locals = globals["__nasal_console_locals"];
|
||||
var numtabs = size(dlg.getChildren("code"));
|
||||
if (!numtabs) {
|
||||
numtabs = 10;
|
||||
}
|
||||
|
||||
var dump = func {
|
||||
rule = "--------------------------------------------------------------------------------";
|
||||
print(rule ~ "\n");
|
||||
print(edit.getValue());
|
||||
print(rule);
|
||||
}
|
||||
|
||||
var clear = func {
|
||||
edit.setValue("");
|
||||
}
|
||||
|
||||
var execute = func(what = nil) {
|
||||
var tag = "<nasal-console>";
|
||||
var err = [];
|
||||
if (what == nil) {
|
||||
what = edit;
|
||||
}
|
||||
var f = call(func { compile(what.getValue(), tag) }, nil, nil, nil, err);
|
||||
if (size(err)) {
|
||||
print(tag ~ ": " ~ err[0]);
|
||||
return;
|
||||
}
|
||||
f = bind(f, globals);
|
||||
call(f, nil, nil, locals, err);
|
||||
if (size(err)) {
|
||||
printf("%s at %s line %d\n", err[0], err[1], err[2]);
|
||||
for (var i = 3; i < size(err); i += 2) {
|
||||
printf(" called from %s line %d\n", err[i], err[i + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tabs = cmdarg().getNode("group[1]");
|
||||
var select = func(which, init = 0) {
|
||||
if (active) { # false in help mode
|
||||
dlg.getNode("active").setIntValue(active);
|
||||
if (!init) {
|
||||
dlg.getChild("code", active).setValue(screen.trim(edit.getValue()));
|
||||
}
|
||||
}
|
||||
if (kbdctrl.getValue()) {
|
||||
execute(dlg.getChild("code", which));
|
||||
return;
|
||||
}
|
||||
active = which;
|
||||
foreach (var c; dlg.getChildren("tab-down")) {
|
||||
c.setBoolValue(c.getIndex() == active);
|
||||
}
|
||||
# animate tab buttons
|
||||
dlg.getNode("active").setIntValue(active = which);
|
||||
edit.setValue(dlg.getChild("code", active).getValue());
|
||||
}
|
||||
|
||||
var help = func {
|
||||
active = 0;
|
||||
foreach (var c; dlg.getChildren("tab-down")) {
|
||||
c.setBoolValue(0);
|
||||
}
|
||||
edit.setValue("Keys:\n"
|
||||
~ " tab ... leave edit mode (visible text cursor)\n"
|
||||
~ " return ... execute active code\n"
|
||||
~ " ctrl-c ... clear input field\n"
|
||||
~ " ctrl-p ... print input field contents to terminal\n"
|
||||
~ " escape ... close dialog\n\n"
|
||||
~ "Ctrl-click on tab buttons executes code without switching to the tab.\n"
|
||||
~ "Add more <code> properties in ~/.fgfs/autosave.xml for more tab buttons.");
|
||||
}
|
||||
|
||||
# setup tab buttons and properties from the template
|
||||
tabs.removeChildren("button");
|
||||
var template = tabs.getNode("button-template");
|
||||
var d = dlg.getPath();
|
||||
for (var i = 1; i <= numtabs; i += 1) {
|
||||
var button = tabs.getChild("button", i, 1);
|
||||
var state = dlg.getChild("tab-down", i, 1);
|
||||
state.setBoolValue(0);
|
||||
|
||||
props.copy(template, button);
|
||||
button.getNode("hide").setValue(0);
|
||||
button.getNode("legend").setIntValue(i);
|
||||
button.getNode("binding[1]/script").setValue("select(" ~ i ~ ")");
|
||||
button.getNode("property").setValue(state.getPath());
|
||||
var c = dlg.getChild("code", i);
|
||||
if (c == nil or c.getType() == "NONE") {
|
||||
c = dlg.getChild("code", i, 1);
|
||||
c.setValue("");
|
||||
c.setAttribute("userarchive", 1);
|
||||
}
|
||||
}
|
||||
|
||||
edit.setValue("");
|
||||
var active = dlg.getNode("active", 1).getValue();
|
||||
if (active == nil) {
|
||||
active = 1;
|
||||
}
|
||||
select(active, 1);
|
||||
</open>
|
||||
</nasal>
|
||||
</PropertyList>
|
||||
|
|
Loading…
Add table
Reference in a new issue