Skip to content

Commit 67609df

Browse files
committed
Much work on editor, as well as key module. Now have access to a full set
of control key combos (of which we're currently using only a few).
1 parent fad9d2d commit 67609df

File tree

3 files changed

+189
-51
lines changed

3 files changed

+189
-51
lines changed

M1/M1API.cs

+61-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This static class implements the APIs that extend MiniScript with
55

66
using System;
77
using System.Collections.Generic;
8+
using System.Globalization;
89
using Miniscript;
910
using Microsoft.Xna.Framework;
1011
using StardewModdingAPI;
@@ -942,6 +943,7 @@ static OpenFile GetOpenFile(TAC.Context context, out string err) {
942943

943944
static ValList keyNames = null;
944945
static ValMap keyModule;
946+
static Dictionary<string, SButton> keyNameMap;
945947
static ValMap KeyModule() {
946948
if (keyModule != null) return keyModule;
947949
keyModule = new ValMap();
@@ -1005,16 +1007,68 @@ static ValMap KeyModule() {
10051007
f.code = (context, partialResult) => {
10061008
string keyName = context.GetLocalString("keyName");
10071009
SButton button = SButton.None;
1008-
switch (keyName) {
1009-
case "left ctrl": button = SButton.LeftControl; break;
1010-
case "right ctrl": button = SButton.RightControl; break;
1011-
case "left alt": button = SButton.LeftAlt; break;
1012-
case "right alt": button = SButton.RightAlt; break;
1013-
// ToDo: complete mappings (probably via a dictionary)
1010+
if (keyNameMap == null) {
1011+
// Note: in SMAPI, use test_input to check key codes
1012+
keyNameMap = new Dictionary<string, SButton>();
1013+
keyNameMap["left ctrl"] = SButton.LeftControl;
1014+
keyNameMap["right ctrl"] = SButton.RightControl;
1015+
keyNameMap["left alt"] = SButton.LeftAlt;
1016+
keyNameMap["right alt"] = SButton.RightAlt;
1017+
keyNameMap["left shift"] = SButton.LeftShift;
1018+
keyNameMap["right shift"] = SButton.RightShift;
1019+
keyNameMap["up"] = SButton.Up;
1020+
keyNameMap["down"] = SButton.Down;
1021+
keyNameMap["left"] = SButton.Left;
1022+
keyNameMap["right"] = SButton.Right;
1023+
for (int num=0; num<=9; num++) {
1024+
keyNameMap[num.ToString(CultureInfo.InvariantCulture)] = SButton.D0 + num;
1025+
keyNameMap["[" + num + "]"] = SButton.NumPad0 + num;
1026+
}
1027+
for (char c='a'; c<='z'; c++) {
1028+
keyNameMap[c.ToString()] = SButton.A + (c - 'a');
1029+
}
1030+
for (int f=1; f<=15; f++) {
1031+
keyNameMap["f" + f] = SButton.F1 + (f - 1);
1032+
}
1033+
keyNameMap["-"] = SButton.OemMinus;
1034+
keyNameMap["="] = SButton.OemPlus;
1035+
keyNameMap["["] = SButton.OemOpenBrackets;
1036+
keyNameMap["]"] = SButton.OemCloseBrackets;
1037+
keyNameMap["\\"] = SButton.OemPipe;
1038+
keyNameMap[","] = SButton.OemComma;
1039+
keyNameMap["."] = SButton.OemPeriod;
1040+
keyNameMap["/"] = SButton.OemQuestion;
1041+
keyNameMap[";"] = SButton.OemSemicolon;
1042+
keyNameMap["'"] = SButton.OemQuotes;
1043+
keyNameMap["`"] = SButton.OemTilde;
1044+
keyNameMap["[+]"] = SButton.Add;
1045+
keyNameMap["[-]"] = SButton.Subtract;
1046+
keyNameMap["[*]"] = SButton.Multiply;
1047+
keyNameMap["[/]"] = SButton.Divide;
1048+
keyNameMap["equals"] = SButton.Execute;
1049+
keyNameMap["clear"] = SButton.OemClear;
1050+
keyNameMap["backspace"] = SButton.Back;
1051+
keyNameMap["tab"] = SButton.Tab;
1052+
keyNameMap["return"] = SButton.Enter;
1053+
keyNameMap["enter"] = SButton.Enter;
1054+
keyNameMap["escape"] = SButton.Escape;
1055+
keyNameMap["space"] = SButton.Space;
1056+
keyNameMap["delete"] = SButton.Delete;
1057+
keyNameMap["insert"] = SButton.Insert;
1058+
keyNameMap["home"] = SButton.Home;
1059+
keyNameMap["end"] = SButton.End;
1060+
keyNameMap["page up"] = SButton.PageUp;
1061+
keyNameMap["page down"] = SButton.PageDown;
1062+
keyNameMap["mouse 0"] = SButton.MouseLeft;
1063+
keyNameMap["mouse 1"] = SButton.MouseRight;
1064+
keyNameMap["mouse 2"] = SButton.MouseMiddle;
1065+
keyNameMap["mouse 3"] = SButton.MouseX1;
1066+
keyNameMap["mouse 4"] = SButton.MouseX2;
10141067
}
1068+
keyNameMap.TryGetValue(keyName, out button);
10151069
if (button == SButton.None) throw new RuntimeException($"Invalid key name: {keyName}");
10161070
bool result = ModEntry.instance.Helper.Input.IsDown(button);
1017-
return new Intrinsic.Result(ValNumber.Truth(result));
1071+
return result ? Intrinsic.Result.True : Intrinsic.Result.False;
10181072
};
10191073
keyModule["pressed"] = f.GetFunc();
10201074

M1/Shell.cs

+10-1
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,18 @@ the user.
1515

1616
namespace Farmtronics {
1717
public class Shell {
18+
static Value _bootOpts = new ValString("bootOpts");
19+
static Value _controlC = new ValString("controlC");
1820
public Console console { get; private set; }
1921
public Bot bot { get; private set; }
20-
public bool allowControlCBreak = true;
22+
public bool allowControlCBreak {
23+
get {
24+
ValMap bootOpts = env.Lookup(_bootOpts) as ValMap;
25+
if (bootOpts == null) return true;
26+
Value v = bootOpts.Lookup(_controlC);
27+
return v == null || v.BoolValue();
28+
}
29+
}
2130

2231
public static Shell runningInstance;
2332

M1/assets/sysdisk/lib/editor.ms

+118-43
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ drawTopBar = function()
3131
text.color = pal.topBarText
3232
text.row = screenH-1; text.column = 0
3333
print " " + _sourceFile
34-
print " " * (screenW - 20 - text.column)
35-
print "^X: Exit Line: " + cursorY
34+
print " " * (screenW - 17 - text.column)
35+
print "^Q: Quit L: " + (cursorY + 1)
3636
print " " * (screenW - text.column)
3737
end function
3838

@@ -43,7 +43,7 @@ refreshLine = function(lineNum)
4343
text.color = pal.text
4444
text.row = screenLines - 1 - y; text.column = 0
4545
if lineNum < data.len then
46-
s = data[lineNum].replace(TAB, " ")[scrollX:]
46+
s = data[lineNum][scrollX:]
4747
if s.len < screenW then s = s + " " * (screenW - s.len)
4848
if s.len > screenW then s = s[:screenW]
4949
else
@@ -70,21 +70,24 @@ printLine = function(row, s)
7070
end if
7171
end function
7272

73-
refreshDisplay = function()
74-
row = screenLines - 1
75-
i = scrollY
73+
refreshRow = function(screenRow)
7674
text.backColor = pal.background
7775
text.color = pal.text
78-
while i < scrollY + screenLines
79-
text.row = row; text.column = 0
80-
if i < data.len then
81-
s = data[i].replace(TAB, " ")[scrollX:]
82-
else
83-
s = ""
84-
end if
85-
printLine row, s
86-
row = row - 1; i = i + 1
87-
end while
76+
i = scrollY + screenLines - 1 - screenRow
77+
text.row = screenRow; text.column = 0
78+
if i < data.len then
79+
s = data[i][scrollX:]
80+
else
81+
s = ""
82+
end if
83+
printLine screenRow, s
84+
if i == cursorY then outer.cursorShown = false
85+
end function
86+
87+
refreshDisplay = function()
88+
for row in range(screenLines-1, 0)
89+
refreshRow row
90+
end for
8891
outer.cursorShown = false
8992
text.backColor = color.clear
9093
end function
@@ -178,20 +181,71 @@ nextWord = function(dir)
178181
outer.cursorX = x
179182
end function
180183

181-
handleKey = function(k)
184+
showCommand = function(keyPress, desc)
185+
refreshRow 0
186+
s = keyPress
187+
if desc then s = s + ": " + desc
188+
text.row = 0
189+
text.column = screenW - s.len
190+
text.color = color.black
191+
text.backColor = "#FFFFAA"
192+
print s[:-1]
193+
text.setCell screenW-1, 0, s[-1]
194+
text.setCellColor screenW-1, 0, text.color
195+
text.setCellBackColor screenW-1, 0, text.backColor
196+
end function
197+
198+
handleControlKey = function(k)
182199
kcode = k.code
183-
184-
byWord = false
185-
// ToDo:
186-
byWord = key.pressed("left ctrl") or key.pressed("right ctrl") or
187-
key.pressed("left alt") or key.pressed("right alt")
188-
189-
hideCursor
190-
if kcode == 1 then // ctrl-A (start of line)
200+
desc = "(undefined)"
201+
202+
anyAlt = key.pressed("left alt") or key.pressed("right alt")
203+
anyShift = key.pressed("left shift") or key.pressed("right shift")
204+
205+
keyPress = "^" + char(64 + kcode)
206+
if kcode > 31 then keyPress = "^" + char(kcode)
207+
if anyShift then keyPress = "Shift-" + keyPress
208+
209+
if keyPress == "^A" then // ctrl-A (start of line)
210+
desc = "LineStart"
191211
outer.cursorX = 0
192-
else if kcode == 5 then // ctrl-E (end of line)
212+
else if keyPress == "^E" then // ctrl-E (end of line)
213+
desc = "LineEnd"
193214
outer.cursorX = lineLen(cursorY)
194-
else if kcode == 17 then // left
215+
else if keyPress == "^U" then // Page Up
216+
desc = "PageUp"
217+
outer.cursorY = outer.cursorY - screenLines
218+
else if keyPress == "^D" then // Page Down
219+
desc = "PageDown"
220+
outer.cursorY = outer.cursorY + screenLines
221+
else if keyPress == "^Q" then // Escape or Ctrl+X
222+
desc = "Quit"
223+
outer.quitting = true
224+
end if
225+
226+
if keyPress then showCommand keyPress, desc
227+
end function
228+
229+
handleKey = function(k, fakeControl=false)
230+
anyCtrl = key.pressed("left ctrl") or key.pressed("right ctrl") or fakeControl
231+
anyAlt = key.pressed("left alt") or key.pressed("right alt")
232+
byWord = anyAlt or anyCtrl
233+
kcode = k.code
234+
hideCursor
235+
236+
// Careful with 17-21: these are generated by the arrow keys,
237+
// which may be used in conjunction with Control, but still
238+
// should be treated differently than ^Q, ^R, etc.
239+
isArrowKey = false
240+
if kcode >= 17 and kcode <=20 then
241+
keys = ["left", "right", "up", "down"]
242+
if key.pressed(keys[kcode-17]) then isArrowKey = true
243+
end if
244+
245+
if anyCtrl and not isArrowKey then
246+
handleControlKey k
247+
else if kcode == 17 then // left
248+
keyPress = ""
195249
outer.cursorX = cursorX - 1
196250
if cursorX < 0 and cursorY > 0 then
197251
outer.cursorY = cursorY - 1
@@ -200,6 +254,7 @@ handleKey = function(k)
200254
if byWord then nextWord -1
201255
outer.idealCursorX = cursorX
202256
else if kcode == 18 then // right
257+
keyPress = ""
203258
outer.cursorX = cursorX + 1
204259
if cursorX > lineLen(cursorY) and cursorY < data.len then
205260
outer.cursorY = cursorY + 1
@@ -208,14 +263,17 @@ handleKey = function(k)
208263
if byWord then nextWord 1
209264
outer.idealCursorX = cursorX
210265
else if kcode == 19 then // up
266+
keyPress = ""
211267
if byWord then amount = screenLines else amount = 1
212268
outer.cursorY = cursorY - amount
213269
outer.cursorX = idealCursorX
214270
else if kcode == 20 then // down
271+
keyPress = ""
215272
if byWord then amount = screenLines else amount = 1
216273
outer.cursorY = cursorY + amount
217274
outer.cursorX = idealCursorX
218275
else if kcode == 10 then // return
276+
keyPress = ""
219277
if cursorY >= data.len then
220278
data.push ""
221279
else
@@ -226,6 +284,7 @@ handleKey = function(k)
226284
outer.cursorX = 0
227285
refreshDisplay
228286
else if kcode == 127 then // forward-delete
287+
keyPress = ""
229288
if cursorX >= data[cursorY].len then
230289
if cursorY < data.len-1 then
231290
data[cursorY] = data[cursorY] + data[cursorY+1]
@@ -241,6 +300,7 @@ handleKey = function(k)
241300
refreshLine cursorY
242301
end if
243302
else if kcode == 8 then // backspace
303+
keyPress = ""
244304
if cursorX > 0 then
245305
endPos = cursorX
246306
outer.cursorX = cursorX - 1
@@ -257,14 +317,13 @@ handleKey = function(k)
257317
outer.cursorX = x
258318
refreshDisplay
259319
end if
260-
else if kcode == 27 or kcode == 24 then // Escape or Ctrl+X
320+
else if kcode == 27 then // Escape
261321
outer.quitting = true
262-
else if kcode == 9 then // tab
263-
// for now, we'll just have tab insert one or two spaces
264-
handleKey " "
265-
if cursorX % 2 then handleKey " "
266-
return
267-
else if kcode > 31 then // printable key
322+
else if k >= " " then // printable key
323+
anyAlt = key.pressed("left alt") or key.pressed("right alt")
324+
anyShift = key.pressed("left shift") or key.pressed("right shift")
325+
byWord = anyCtrl or anyAlt
326+
268327
if cursorY >= data.len then
269328
data.push k
270329
else if not data[cursorY] then
@@ -275,22 +334,32 @@ handleKey = function(k)
275334
outer.cursorX = cursorX + 1
276335
refreshLine cursorY
277336
end if
337+
278338
limitCursor
279339
scrollCursorIntoView
280340
drawTopBar
281-
showCursor
282-
283-
// for development & debugging:
284-
// text.row = screenH-1; text.column = screenW/2
285-
// text.backColor = pal.topBar; text.color = "#888888"
286-
// print kcode + (" byWord" * byWord + " " * (not byWord))
287-
341+
showCursor
288342
end function
289343

290344
update = function()
291345
wantCursor = (time % 1 < 0.75)
292346
if wantCursor != cursorShown then showCursor wantCursor
293-
if key.available then handleKey key.get
347+
if key.available then
348+
handleKey key.get
349+
else if key.pressed("left ctrl") or key.pressed("right ctrl") then
350+
for c in "1234567890[]/=\;-',.`"
351+
if key.pressed(c) then
352+
while key.pressed(c); yield; end while
353+
handleControlKey c
354+
end if
355+
end for
356+
else if key.pressed("page up") then
357+
while key.pressed("page up"); yield; end while
358+
handleKey "U", true
359+
else if key.pressed("page down") then
360+
while key.pressed("page down"); yield; end while
361+
handleKey "D", true
362+
end if
294363
end function
295364

296365
// editText: main entry point.
@@ -299,7 +368,8 @@ editText = function(textToEdit)
299368
if textToEdit == null then textToEdit = []
300369
if textToEdit.len == 0 then textToEdit.push ""
301370
outer.data = textToEdit
302-
371+
// We don't handle tabs properly yet, so let's just:
372+
for i in data.indexes; data[i] = data[i].replace(TAB, " "); end for
303373
origColor = text.color
304374
origBackColor = text.backColor
305375
text.backColor = color.clear
@@ -308,13 +378,18 @@ editText = function(textToEdit)
308378
drawTopBar
309379
refreshDisplay
310380

381+
if not env.hasIndex("bootOpts") then env.bootOpts = {}
382+
env.bootOpts.controlC = false // disable Control-C to break
383+
311384
outer.quitting = false
312385
while not quitting
313386
yield
314387
update
315388
end while
316389
key.clear
317390

391+
env.bootOpts.controlC = true // re-eable Control-C to break
392+
318393
text.delimiter = char(13)
319394
text.color = origColor
320395
text.backColor = origBackColor

0 commit comments

Comments
 (0)