forked from ozin370/Script
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib_menu.ks
406 lines (351 loc) · 13.7 KB
/
lib_menu.ks
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
// For this to work you need to create at least one menu list in this format (note the extra value at the end of numbers!):
//
// local sv is -9.9993134.
// set someMenu to list(
// list("Some text to display", "text"),
// list("_", "line"),
// list("A read-only variable:", "display", { return round(ship:airspeed,1) + " m/s". }),
// list("A number:", "number", { parameter p is sv. if p <> sv set number1 to p. return number1. }, 10),
// list("Editable text:", "string", { parameter p is sv. if p <> sv set string1 to p. return string1. }),
// list("Boolean value:", "bool", { parameter p is sv. if p <> sv toggle bool1. return bool1. }),
// list("An action", "action", { stage. }),
// list("Some other menu", "menu", { return someSubMenu. }),
// list("The parent menu", "backmenu", { return mainMenu. })
// ).
//
// The first three types are read-only items and can't be selected or changed by the program
// Available types:
// text:
// just displays the name of the item, can print across all columns.
// line:
// makes a horizontal line across the menu using the single character.
// display:
// displays what you return from the anonymous function
// number:
// Can be edited with A/D (in increments) with Q/E altering increment scale.
// Can be replaced by starting to type in a number when the line is selected. Hit enter to apply it. Backspace works too.
// Can be edited by hitting enter with the line selected.
// Needs a get/set anonymous function like shown above.
// Needs the variable to have been declared by you elsewhere in your main program.
// Needs to have a increment multiplier set in it's list as shown above (a value of 10 will make the default increment 10).
// string:
// Can be edited by hitting enter with the line selected.
// Needs a get/set anonymous function like shown above.
// Needs the variable to have been declared by you elsewhere in your main program.
// bool:
// Can be toggled by hitting enter, A, or D
// Needs a get/set anonymous function like shown above.
// Needs the variable to have been declared by you elsewhere in your main program.
// action:
// Runs the anonymous function when you hit enter/D when it is selected
// menu:
// Opens another menu that you will have to link to in the function.
// backmenu:
// exactly like "menu" but this can also be triggered by pressing backspace
// Controls:
// W/S/UpArrow/DownArrow: navigates the menu
// A/D: Runs commands, increments numbers
// Q/E: Changes number increment scale
// Enter: Runs commands, edits numbers and strings, toggles boolean values.
// Backspace: opens the parent menu if it exists (for submenues)
// Typing a number: When a number is selected, typing a number will start the number editor. Enter or W/S to confirm.
//lib_menu.ks
@LAZYGLOBAL on.
runoncepath("lib_UI.ks"). //needed for some terminal drawing functions.
if not (defined startLine) local startLine is 0. //the first menu item will start at this line in the terminal window
if not (defined startColumn) local startColumn is 3. //menu item description starts at this x coordinate, remember to leave some space for the marker on the left
if not (defined nameLength) local nameLength is 16. //how many characters of the menu item names to display
if not (defined valueLength) local valueLength is 12. //how many characters of the menu item values to display
if not (defined incrOptions) local incrOptions is list (0.01,0.1,1,10,100,1000). // default increment values in ascending order (for incrementing numbers with A/D). Feel free to add or remove steps from this list
local incrI is 2.
local typingNumber is false.
local typingString is false.
local selectedLine is 0.
local lineType is "text".
local dummyMenu is list(
list("","text"),
list("This is a placeholder menu, if you see this you need to","text"),
list("set the activeMenu variable to a menu list of your own","text"),
list("","text"),
list("<what?>","action",{ hudtext("It puts the menu in the variable or else it gets the hose again",5, 4, 35, red, false). })
).
// !! activeMenu is a varaible that should contain the list that defines the menu that you want to display !!
if not (defined activeMenu) global activeMenu is dummyMenu.
local lastMenu is activeMenu.
// ### Drawing stuff to terminal ###
// >>
//draw the menu
function drawAll {
terminal:input:clear(). //clear inputs for new menus just in case..
set selectedLine to 0.
set lineType to activeMenu[selectedLine][1].
//column starts
set C1 to startColumn.
set C2 to C1 + nameLength.
set C3 to C2 + valueLength.
clearBox(startLine,startLine + lastMenu:length - 1,C1,C3). //clear the last menu. This won't clear anything else on the terminal
local notFoundSelectable is true.
local i is 0.
inputs().
until i >= activeMenu:length {
if notFoundSelectable and not readOnlyTypes:contains(activeMenu[i][1]) { //skip read only lines
set selectedLine to i.
set notFoundSelectable to false.
}
updateLine(i).
//debug info about your current menu, uncomment to display it below your menu:
//print activeMenu[i][0] + " = " + activeMenu[i][1] at (1,startLine + activeMenu:length + 2 + i).
set i to i + 1.
}
drawMarker().
}
local refreshTypes is list("number","display","string","bool").
function refreshAll { //updates all values of the current menu in the terminal
local i is 0.
until i >= activeMenu:length {
if refreshTypes:contains(activeMenu[i][1]) and not((typingNumber or typingString) and i = selectedLine) {
updateLine(i).
}
set i to i + 1.
}
}
local markerStr is ">> ".
local suffixString is "".
local markerTimer is time:seconds + 900000.
function drawMarker {
print markerStr at (C1-3,startLine + selectedLine). //print the arrow that displays the active selection
//print relevant information at the end of the current line
if lineType = "number" {
set suffixString to "-+" + incrOptions[incrI] * activeMenu[selectedLine][3].
print suffixString at (C3 + 1,startLine + selectedLine).
}
else if lineType = "bool" {
set suffixString to activeMenu[selectedLine][2]():tostring().
print suffixString at (C3 + 1,startLine + selectedLine).
}
else if lineType = "menu" or lineType = "backmenu" {
set suffixString to "Menu".
print suffixString at (C3 + 1,startLine + selectedLine).
}
}
local typesWithSuffix is list("number","bool","menu","backmenu").
function clearMarkers {
print " " at (C1 - markerStr:length,startLine + selectedLine).
if typesWithSuffix:contains(lineType) {
local emptyString is " ".
print emptyString:substring(0,suffixString:length) at (C3 + 1,startLine + selectedLine).
}
}
// <<
// ### Input and value handling ###
// >>
local readOnlyTypes is list("text","display","line").
function inputs {
if typingNumber { //currently recording numbers, so ignore all other commands
updateLine(selectedLine,numberString).
if terminal:input:haschar() {
local inp is terminal:input.
local ch is inp:getchar().
if ch:tonumber(99) <> 99 or ch = "." or (numberString:length = 0 and ch = "-") {
set numberString to numberstring + ch.
}
else if ch = inp:backspace {
if numberString:length > 0 set numberString to numberString:remove(numberString:length-1,1).
}
else if ch = inp:enter or ch = "d" or ch = "w" or ch = "s"{ // confirm
set typingNumber to false.
local converted is numberString:tonumber(-9999).
if converted <> -9999 { //valid
updateLine(selectedLine,numberString).
activeMenu[selectedLine][2](converted). //change the actual variable through the function stored in the lex
}
set numberString to "".
}
else if unchar(ch) = 9 { //tab - cancel and revert
set typingNumber to false.
set numberString to "".
}
}
}
else if typingString { //currently recording text
updateLine(selectedLine,stringString).
if terminal:input:haschar() {
local inp is terminal:input.
local ch is inp:getchar().
if ch = inp:enter { // confirm
set typingString to false.
updateLine(selectedLine,stringString).
activeMenu[selectedLine][2](stringString).
set stringString to "".
}
else if ch = inp:backspace {
if stringString:length > 0 set stringString to stringString:remove(stringString:length-1,1).
}
else if unchar(ch) = 9 { //tab - cancel and revert
set typingString to false.
set stringString to "".
updateLine(selectedLine).
}
else set stringString to stringString + ch.
}
}
else if terminal:input:haschar() { //not recording, so enable all other commands
local inp is terminal:input.
local ch is inp:getchar().
if unchar(ch) = 9 set ch to inp:backspace.
set lineType to activeMenu[selectedLine][1].
local oldLine is selectedLine.
if ch = "w" or ch = inp:upcursorone {
clearMarkers().
set selectedLine to selectedLine - 1.
if selectedLine < 0 set selectedLine to activeMenu:length - 1.
//until activeMenu[selectedLine][1] <> "text" and activeMenu[selectedLine][1] <> "line" { //skip text lines as they have no values
until not readOnlyTypes:contains(activeMenu[selectedLine][1]) {
set selectedLine to selectedLine - 1.
if selectedLine < 0 set selectedLine to activeMenu:length - 1.
}
set incrI to 2.
updateLine(oldLine).
}
else if ch = "s" or ch = inp:downcursorone {
clearMarkers().
set selectedLine to selectedLine + 1.
if selectedLine >= activeMenu:length set selectedLine to 0.
//until activeMenu[selectedLine][1] <> "text" and activeMenu[selectedLine][1] <> "line" { //skip text lines as they have no values
until not readOnlyTypes:contains(activeMenu[selectedLine][1]) {
set selectedLine to selectedLine + 1.
if selectedLine >= activeMenu:length set selectedLine to 0.
}
set incrI to 2.
updateLine(oldLine).
}
else if ch = "a" or ch = inp:leftcursorone adjust(-1).
else if ch = "d" or ch = inp:rightcursorone adjust(1).
else if ch = "q" { set incrI to max(0,incrI - 1). }
else if ch = "e" { set incrI to min(incrOptions:length - 1,incrI + 1). }
else if lineType = "number" and (ch:tonumber(99) <> 99 or ch = "-" or ch = inp:enter) {
set typingNumber to true.
if ch = inp:enter set numberString to activeMenu[selectedLine][2]():tostring().
else set numberString to ch.
}
else if ch = inp:enter {
if lineType = "menu" or lineType = "backmenu" or lineType = "bool" or lineType = "action" adjust(0).
else if lineType = "string" {
set stringString to activeMenu[selectedLine][2]().
set typingString to true.
}
//else if lineType = "action" activeMenu[selectedLine][2]().
}
else if ch = inp:backspace {
local i is 0.
until i >= activeMenu:length { //find the first "backmenu" item if it exists and trigger that
if activeMenu[i][1] = "backmenu" {
clearMarkers().
set selectedLine to i.
adjust(0).
set i to activeMenu:length.
}
set i to i + 1.
}
}
set lineType to activeMenu[selectedLine][1].
clearMarkers().
drawMarker().
updateLine(selectedLine).
inp:clear().
}
else if time:seconds > markerTimer + 0.2 {
set markerStr to ">> ".
set markerTimer to time:seconds + 900000.
drawMarker().
}
}
local nonPaddedTypes is list("text","action","menu","backmenu").
set blink to 0.
function updateLine {
parameter line,val is "x".
local nameStr is activeMenu[line][0].
local valType is activeMenu[line][1].
local finalStr is "".
if nonPaddedTypes:contains(valType) {
set finalStr to nameStr.
}
else if valType = "line" {
set finalStr to "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".
local tableWidth is C3-C1.
set finalStr to finalStr:substring(0,tableWidth).
set finalStr to finalStr:replace("x",nameStr).
}
else {
if val = "x" {
set val to activeMenu[line][2]().
//if valType = "number" set val to val:tostring().
}
local blinkStr is "".
if (typingNumber or typingString) and line = selectedLine { //blinking cursor when typing in
if blink < 10 set blinkStr to "_".
else set blinkStr to " ".
set blink to blink + 1.
if blink > 20 set blink to 0.
}
local valStr is "".
if valType = "bool" {
if val set valStr to "[ X ]".
else set valStr to "[ ]".
}
else if valType = "number" {
//if not typingNumber and val:tonumber(-99.9999) <> -99.9999 and val:tonumber() > 9999 set valStr to round(val:tonumber() / 1000,1) + "K".
set val to val + blinkStr.
if not typingNumber and abs(val:tonumber()) > 9999 set valStr to round(val:tonumber() / 1000,1) + " K".
else set valStr to val.
}
else if valType = "string" or valType = "display" {
if typingString {
set val to val + blinkStr.
set valStr to val:substring(max(0,val:length - (C3-C2)),min(val:length,C3-C2)).
}
else {
if not val:istype("string") set val to val:tostring().
set valStr to val:substring(0,min(val:length,C3-C2)).
}
}
set valStr to valStr:padright(C3-C2).
set finalStr to nameStr:padright(C2-C1):substring(0,C2-C1) + valStr.
}
print finalStr at (C1,startLine + line).
}
function adjust {
parameter sign.
local func is activeMenu[selectedLine][2].
set lineType to activeMenu[selectedline][1].
set markerStr to "> >".
set markerTimer to time:seconds.
if lineType = "number" {
func(func() + sign * incrOptions[incrI] * activeMenu[selectedLine][3]).
}
else if lineType = "bool" {
//if sign = -1 func(true).
//else func(false).
func(sign).
}
else if lineType = "menu" or lineType = "backmenu" { // d
setMenu(func()).
set markerTimer to 0.
}
else if lineType = "action" func().
}
function setMenu {
parameter menu.
clearMarkers().
set lastMenu to activeMenu.
set activeMenu to menu.
drawAll().
set markerTimer to 0.
}
function boolConvert {
parameter val.
if val = -1 set val to false.
else set val to true.
return val.
}
// <<