commit d4b8ce3eb624ce88301cc6a3418b7e6d8e661f86
parent 588466c2739d21bfdb8a70f2e2c5727900de4bc6
Author: Skylar Hill <stellarskylark@posteo.net>
Date: Fri, 3 Jun 2022 15:20:02 -0500
Start implementing command mode, switch description-scroll bindings to brackets, fix empty window crash bug
Diffstat:
3 files changed, 143 insertions(+), 60 deletions(-)
diff --git a/src/tui.nim b/src/tui.nim
@@ -10,6 +10,69 @@ import
import utils, verb
+type
+ Mode = enum
+ Normal,
+ Command
+
+proc toAscii(key: Key): string =
+ result =
+ case key
+ of Key.A: "a"
+ of Key.B: "b"
+ of Key.C: "c"
+ of Key.D: "d"
+ of Key.E: "e"
+ of Key.F: "f"
+ of Key.G: "g"
+ of Key.H: "h"
+ of Key.I: "i"
+ of Key.J: "j"
+ of Key.K: "k"
+ of Key.L: "l"
+ of Key.M: "m"
+ of Key.N: "n"
+ of Key.O: "o"
+ of Key.P: "p"
+ of Key.Q: "q"
+ of Key.R: "r"
+ of Key.S: "s"
+ of Key.T: "t"
+ of Key.U: "u"
+ of Key.V: "v"
+ of Key.W: "w"
+ of Key.X: "x"
+ of Key.Y: "y"
+ of Key.Z: "z"
+ of Key.ShiftA: "A"
+ of Key.ShiftB: "B"
+ of Key.ShiftC: "C"
+ of Key.ShiftD: "D"
+ of Key.ShiftE: "E"
+ of Key.ShiftF: "F"
+ of Key.ShiftG: "G"
+ of Key.ShiftH: "H"
+ of Key.ShiftI: "I"
+ of Key.ShiftJ: "J"
+ of Key.ShiftK: "K"
+ of Key.ShiftL: "L"
+ of Key.ShiftM: "M"
+ of Key.ShiftN: "N"
+ of Key.ShiftO: "O"
+ of Key.ShiftP: "P"
+ of Key.ShiftQ: "Q"
+ of Key.ShiftR: "R"
+ of Key.ShiftS: "S"
+ of Key.ShiftT: "T"
+ of Key.ShiftU: "U"
+ of Key.ShiftV: "V"
+ of Key.ShiftW: "W"
+ of Key.ShiftX: "X"
+ of Key.ShiftY: "Y"
+ of Key.ShiftZ: "Z"
+ of Key.Space: " "
+ else: ""
+
proc placeWindows(tb: TerminalBuffer, windows: seq[YamlNode], descY: int): Table[string, (int, int, int, int)] =
result = initTable[string, (int, int, int, int)]()
let windowWidth =
@@ -41,6 +104,7 @@ proc runTui*(sheet: YamlDocument): void =
var itemsByWindow = initTable[string, seq[string]]()
var statusText = ""
var descScroll = 0
+ var mode = Normal
# Initialize tabs, windows, and expressions
for tab in tabs:
@@ -78,37 +142,51 @@ proc runTui*(sheet: YamlDocument): void =
let win = tabWindows[selWinIndex].content
var key = getKey()
- case key
- of Key.Q, Key.Escape: exitProc()
- of Key.ShiftJ:
- tabIndex = tabIndex.loopInc(tabs.elems)
- selWinIndex = 0
- of Key.ShiftK:
- tabIndex = tabIndex.loopDec(tabs.elems)
- selWinIndex = 0
- of Key.L:
- selWinIndex = selwinIndex.loopInc(tabWindows)
- descScroll = 0
- of Key.H:
- selWinIndex = selwinIndex.loopDec(tabWindows)
- descScroll = 0
- of Key.J:
- selectedByWindow[win] = selectedByWindow[win].loopInc(itemsByWindow[win])
- descScroll = 0
- of Key.K:
- selectedByWindow[win] = selectedByWindow[win].loopDec(itemsByWindow[win])
- descScroll = 0
- of Key.Enter:
- let itemList = itemsByWindow[win]
- let action = itemList[selectedByWindow[win]]
- statusText = fmt"{fancyDisplay(action, 99)}: {doVerb(action, Roll, sheet)}"
- of Key.CtrlJ, Key.PageDown:
- descScroll += 1
- of Key.CtrlK, Key.PageUp:
- descScroll =
- if descScroll == 0: 0
- else: descScroll - 1
- else: discard
+ if mode == Command:
+ case key
+ of Key.Escape:
+ mode = Normal
+ statusText = ""
+ of Key.Enter:
+ mode = Normal
+ of Key.Backspace: statusText = statusText[0..^2]
+ else:
+ statusText &= key.toAscii()
+ else:
+ case key
+ of Key.Q, Key.Escape: exitProc()
+ of Key.ShiftJ, Key.Tab:
+ tabIndex = tabIndex.loopInc(tabs.elems)
+ selWinIndex = 0
+ of Key.ShiftK:
+ tabIndex = tabIndex.loopDec(tabs.elems)
+ selWinIndex = 0
+ of Key.L, Key.Right:
+ selWinIndex = selwinIndex.loopInc(tabWindows)
+ descScroll = 0
+ of Key.H, Key.Left:
+ selWinIndex = selwinIndex.loopDec(tabWindows)
+ descScroll = 0
+ of Key.J, Key.Down:
+ selectedByWindow[win] = selectedByWindow[win].loopInc(itemsByWindow[win])
+ descScroll = 0
+ of Key.K, Key.Up:
+ selectedByWindow[win] = selectedByWindow[win].loopDec(itemsByWindow[win])
+ descScroll = 0
+ of Key.Enter:
+ let itemList = itemsByWindow[win]
+ let action = itemList[selectedByWindow[win]]
+ statusText = fmt"{fancyDisplay(action, 99)}: {doVerb(action, Roll, sheet)}"
+ of Key.RightBracket, Key.PageDown:
+ descScroll += 1
+ of Key.LeftBracket, Key.PageUp:
+ descScroll =
+ if descScroll == 0: 0
+ else: descScroll - 1
+ of Key.Colon:
+ mode = Command
+ statusText = ":"
+ else: discard
# Write title
let title = fmt"""{sheet.root.getContent("name")}, {sheet.root.getContent("class")} {sheet.root.getContent("level")}"""
@@ -170,25 +248,27 @@ proc runTui*(sheet: YamlDocument): void =
# Write description
bb.drawRect(1, descY, tb.width - 1, tb.height - 1, doubleStyle = true)
let selWin = tabWindows[selWinIndex].content
- let sel = itemsByWindow[selWin][selectedByWindow[selWin]]
- let item = sheet.getExpression(sel)
- let desc = item.getContent("desc")
- let val = (item.getContent("dice") & " " & item.getContent("modifier")).strip
- let roll =
- if not item.contains("dice"):
- let rolled = doVerb(sel, Roll, sheet)
- if rolled != item.getContent("modifier"):
- rolled
- else: "hardcoded"
- else: "Press ENTER to roll"
- tb.write(3, descY + 1, fmt"Value: {val} [{roll}]")
- if desc != "":
- let formattedDesc = desc.wrapWords(maxLineWidth = min(60, tb.width - 6))
- let lines = formattedDesc.splitLines
- for i in 0..lines.high:
- if i < descScroll:
- continue
- tb.write(3, descY + 2 + i - descScroll, lines[i])
+ let items = itemsByWindow[selWin]
+ if items.len > 0:
+ let sel = items[selectedByWindow[selWin]]
+ let item = sheet.getExpression(sel)
+ let desc = item.getContent("desc")
+ let val = (item.getContent("dice") & " " & item.getContent("modifier")).strip
+ let roll =
+ if not item.contains("dice"):
+ let rolled = doVerb(sel, Roll, sheet)
+ if rolled != item.getContent("modifier"):
+ rolled
+ else: "hardcoded"
+ else: "Press ENTER to roll"
+ tb.write(3, descY + 1, fmt"Value: {val} [{roll}]")
+ if desc != "":
+ let formattedDesc = desc.wrapWords(maxLineWidth = min(60, tb.width - 6))
+ let lines = formattedDesc.splitLines
+ for i in 0..lines.high:
+ if i < descScroll:
+ continue
+ tb.write(3, descY + 2 + i - descScroll, lines[i])
tb.write(bb)
tb.setForegroundColor(fgBlue)
diff --git a/src/verb.nim b/src/verb.nim
@@ -8,7 +8,7 @@ import
import utils
-let reRawExp = re"([a-zA-Z\-]+[a-zA-Z])"
+let reRawExp = re"[a-zA-Z\-']+[a-zA-Z']"
let reProcessedExp = re"^[\d*+\-/()]*$"
let reDice = re"^\d*d\d+$"
@@ -24,7 +24,7 @@ proc expressionAsString(node: YamlNode): string =
result = result & fmt"""Tab: {node.getContent("tab")}""" & "\n"
result = result & fmt"""Window: {node.getContent("window")}"""
-proc calculateModifier(exp: string, sheet: YamlNode): string =
+proc calculateModifier(exp: string, sheet: YamlDocument): string =
var working = exp
if exp.contains(reProcessedExp):
# I think that execCmdEx adds whitespace in input;
@@ -35,8 +35,8 @@ proc calculateModifier(exp: string, sheet: YamlNode): string =
working = output.strip
return working
- if sheet["expressions"].contains exp:
- let subExp = sheet["expressions"][exp]
+ if sheet.root["expressions"].contains exp:
+ let subExp = sheet.getExpression(exp)
working = calculateModifier(subExp.getContent("modifier"), sheet)
return working
@@ -44,7 +44,7 @@ proc calculateModifier(exp: string, sheet: YamlNode): string =
working = working.replace(match, calculateModifier(match, sheet))
return calculateModifier(working, sheet)
-proc roll(action: string, sheet: YamlNode): string =
+proc roll(action: string, sheet: YamlDocument): string =
if action.contains(reDice):
let (output, code) = execCmdEx("rolldice -s", input = action)
if code == 0:
@@ -53,7 +53,7 @@ proc roll(action: string, sheet: YamlNode): string =
var exp: YamlNode
try:
- exp = sheet["expressions"][action]
+ exp = sheet.getExpression(action)
except KeyError:
return action & " is not a dice expression or defined expression"
@@ -76,8 +76,8 @@ proc roll(action: string, sheet: YamlNode): string =
# rolldice returns its input; the actual roll is the second line
result = output.splitLines[1]
-proc view(action: string, sheet: YamlNode): string =
- let node = sheet["expressions"][action]
+proc view(action: string, sheet: YamlDocument): string =
+ let node = sheet.getExpression(action)
echo action & ":"
echo expressionAsString(node)
@@ -89,8 +89,8 @@ proc write(doc: YamlDocument, file: string) =
proc doVerb*(action: string, verb: Verb, sheet: YamlDocument): string =
case verb
of Roll:
- return roll(action, sheet.root)
+ return roll(action, sheet)
of View:
- return view(action, sheet.root)
+ return view(action, sheet)
of Tui:
raise newException(Exception, "Please report this error, this code should never get run.")
diff --git a/templates/dnd5e.yaml b/templates/dnd5e.yaml
@@ -285,3 +285,6 @@ expressions:
prestidigitation:
desc: "Cantrip. This spell is a minor magical trick that novice spellcasters use for practice. You create one of the following magical effects within range: 1) You create an instantaneous, harmless sensory effect, such as a shower of sparks, a puff of wind, faint musical notes, or an odd odor. 2) You instantaneously light or snuff out a candle, a torch, or a small campfire. 3) You instantaneously clean or soil an object no larger than 1 cubic foot. 4) You chill, warm, or flavor up to 1 cubic foot of nonliving material for 1 hour. 5) You make a color, a small mark, or a symbol appear on an object or a surface for 1 hour. 6) You create a nonmagical trinket or an illusory image that can fit in your hand and that lasts until the end of your next turn. If you cast this spell multiple times, you can have up to three of its non-instantaneous effects active at a time, and you can dismiss such an effect as an action."
window: spells
+ carl's-hideous-face:
+ desc: 9th level. Oh god, oh no, look away if you want to live.
+ window: spells