2018-07-22 03:43:01 +00:00
|
|
|
package doodle
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2018-07-24 03:10:53 +00:00
|
|
|
"fmt"
|
2018-07-22 03:43:01 +00:00
|
|
|
"strings"
|
|
|
|
|
2019-04-10 00:35:44 +00:00
|
|
|
"git.kirsle.net/apps/doodle/lib/events"
|
|
|
|
"git.kirsle.net/apps/doodle/lib/render"
|
|
|
|
"git.kirsle.net/apps/doodle/lib/ui"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/balance"
|
|
|
|
"git.kirsle.net/apps/doodle/pkg/log"
|
2019-07-05 23:04:36 +00:00
|
|
|
"git.kirsle.net/apps/doodle/pkg/shmem"
|
2018-07-26 02:38:54 +00:00
|
|
|
"github.com/robertkrimen/otto"
|
2018-07-22 03:43:01 +00:00
|
|
|
)
|
|
|
|
|
2018-07-24 03:10:53 +00:00
|
|
|
// Flash a message to the user.
|
|
|
|
func (d *Doodle) Flash(template string, v ...interface{}) {
|
2018-09-23 22:20:45 +00:00
|
|
|
log.Warn(template, v...)
|
2018-07-24 03:10:53 +00:00
|
|
|
d.shell.Write(fmt.Sprintf(template, v...))
|
|
|
|
}
|
|
|
|
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
// Prompt the user for a question in the dev console.
|
|
|
|
func (d *Doodle) Prompt(question string, callback func(string)) {
|
|
|
|
d.shell.Prompt = question
|
|
|
|
d.shell.callback = callback
|
|
|
|
d.shell.Open = true
|
|
|
|
}
|
|
|
|
|
2018-07-22 03:43:01 +00:00
|
|
|
// Shell implements the developer console in-game.
|
|
|
|
type Shell struct {
|
2018-07-26 02:38:54 +00:00
|
|
|
parent *Doodle
|
|
|
|
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
Open bool
|
|
|
|
Prompt string
|
2018-10-08 20:06:42 +00:00
|
|
|
Repl bool
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
callback func(string) // for prompt answers only
|
|
|
|
Text string
|
|
|
|
History []string
|
|
|
|
Output []string
|
|
|
|
Flashes []Flash
|
2018-07-26 02:38:54 +00:00
|
|
|
|
|
|
|
// Blinky cursor variables.
|
|
|
|
cursor byte // cursor symbol
|
2018-07-22 03:43:01 +00:00
|
|
|
cursorFlip uint64 // ticks until cursor flip
|
|
|
|
cursorRate uint64
|
2018-07-26 02:38:54 +00:00
|
|
|
|
|
|
|
// Paging through history variables.
|
|
|
|
historyPaging bool
|
|
|
|
historyIndex int
|
|
|
|
|
|
|
|
// JavaScript shell interpreter.
|
|
|
|
js *otto.Otto
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Flash holds a message to flash on screen.
|
|
|
|
type Flash struct {
|
|
|
|
Text string
|
|
|
|
Expires uint64 // tick that it expires
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewShell initializes the shell helper (the "Shellper").
|
|
|
|
func NewShell(d *Doodle) Shell {
|
2018-07-26 02:38:54 +00:00
|
|
|
s := Shell{
|
2018-07-22 03:43:01 +00:00
|
|
|
parent: d,
|
|
|
|
History: []string{},
|
|
|
|
Output: []string{},
|
|
|
|
Flashes: []Flash{},
|
|
|
|
Prompt: ">",
|
2018-07-26 02:38:54 +00:00
|
|
|
cursor: '_',
|
2018-07-22 03:43:01 +00:00
|
|
|
cursorRate: balance.ShellCursorBlinkRate,
|
2018-07-26 02:38:54 +00:00
|
|
|
js: otto.New(),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make the Doodle instance available to the shell.
|
|
|
|
bindings := map[string]interface{}{
|
|
|
|
"d": d,
|
2019-04-10 00:35:44 +00:00
|
|
|
"log": log.Logger,
|
2018-07-26 02:38:54 +00:00
|
|
|
"RGBA": render.RGBA,
|
|
|
|
"Point": render.NewPoint,
|
2018-08-02 01:52:52 +00:00
|
|
|
"Rect": render.NewRect,
|
2018-10-08 20:06:42 +00:00
|
|
|
"Tree": func(w ui.Widget) string {
|
|
|
|
for _, row := range ui.WidgetTree(w) {
|
|
|
|
d.Flash(row)
|
|
|
|
}
|
|
|
|
return ""
|
|
|
|
},
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
2018-07-26 02:38:54 +00:00
|
|
|
for name, v := range bindings {
|
|
|
|
err := s.js.Set(name, v)
|
|
|
|
if err != nil {
|
|
|
|
log.Error("Failed to make `%s` available to JS shell: %s", name, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return s
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close the shell, resetting its internal state.
|
|
|
|
func (s *Shell) Close() {
|
|
|
|
log.Debug("Shell: closing shell")
|
|
|
|
s.Open = false
|
2018-10-08 20:06:42 +00:00
|
|
|
s.Repl = false
|
2018-07-22 03:43:01 +00:00
|
|
|
s.Prompt = ">"
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
s.callback = nil
|
2018-07-22 03:43:01 +00:00
|
|
|
s.Text = ""
|
2018-07-26 02:38:54 +00:00
|
|
|
s.historyPaging = false
|
|
|
|
s.historyIndex = 0
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Execute a command in the shell.
|
|
|
|
func (s *Shell) Execute(input string) {
|
|
|
|
command := s.Parse(input)
|
2018-10-08 20:06:42 +00:00
|
|
|
|
2018-07-26 02:38:54 +00:00
|
|
|
if command.Raw != "" {
|
|
|
|
s.Output = append(s.Output, s.Prompt+command.Raw)
|
|
|
|
s.History = append(s.History, command.Raw)
|
|
|
|
}
|
|
|
|
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
// Are we answering a Prompt?
|
|
|
|
if s.callback != nil {
|
|
|
|
log.Info("Invoking prompt callback:")
|
|
|
|
s.callback(command.Raw)
|
|
|
|
s.Close()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2018-07-25 05:26:27 +00:00
|
|
|
if command.Command == "clear" {
|
|
|
|
s.Output = []string{}
|
|
|
|
} else {
|
|
|
|
err := command.Run(s.parent)
|
|
|
|
if err != nil {
|
|
|
|
s.Write(err.Error())
|
|
|
|
}
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset the text buffer in the shell.
|
2018-10-08 20:06:42 +00:00
|
|
|
if s.Repl {
|
|
|
|
s.Text = "$ "
|
|
|
|
} else {
|
|
|
|
s.Text = ""
|
|
|
|
}
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write a line of output text to the console.
|
|
|
|
func (s *Shell) Write(line string) {
|
|
|
|
s.Output = append(s.Output, line)
|
|
|
|
s.Flashes = append(s.Flashes, Flash{
|
|
|
|
Text: line,
|
2019-07-05 23:04:36 +00:00
|
|
|
Expires: shmem.Tick + balance.FlashTTL,
|
2018-07-22 03:43:01 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the command line.
|
|
|
|
func (s *Shell) Parse(input string) Command {
|
|
|
|
input = strings.TrimSpace(input)
|
|
|
|
if len(input) == 0 {
|
|
|
|
return Command{}
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
inQuote bool
|
|
|
|
buffer = bytes.NewBuffer([]byte{})
|
|
|
|
words = []string{}
|
|
|
|
)
|
|
|
|
for i := 0; i < len(input); i++ {
|
|
|
|
char := input[i]
|
|
|
|
switch char {
|
|
|
|
case ' ':
|
|
|
|
if inQuote {
|
|
|
|
buffer.WriteByte(char)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if word := buffer.String(); word != "" {
|
|
|
|
words = append(words, word)
|
|
|
|
buffer.Reset()
|
|
|
|
}
|
|
|
|
case '"':
|
|
|
|
if !inQuote {
|
|
|
|
// An opening quote character.
|
|
|
|
inQuote = true
|
|
|
|
} else {
|
|
|
|
// The closing quote.
|
|
|
|
inQuote = false
|
|
|
|
|
|
|
|
if word := buffer.String(); word != "" {
|
|
|
|
words = append(words, word)
|
|
|
|
buffer.Reset()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
buffer.WriteByte(char)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if remainder := buffer.String(); remainder != "" {
|
|
|
|
words = append(words, remainder)
|
|
|
|
}
|
|
|
|
|
|
|
|
return Command{
|
|
|
|
Raw: input,
|
|
|
|
Command: words[0],
|
|
|
|
Args: words[1:],
|
|
|
|
ArgsLiteral: strings.TrimSpace(input[len(words[0]):]),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the shell.
|
|
|
|
func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
2018-10-28 05:22:13 +00:00
|
|
|
// Compute the line height we can draw.
|
|
|
|
lineHeight := balance.ShellFontSize + int(balance.ShellPadding)
|
2018-10-08 20:06:42 +00:00
|
|
|
|
2018-10-28 05:22:13 +00:00
|
|
|
// If the console is open, draw the console.
|
|
|
|
if s.Open {
|
|
|
|
if ev.EscapeKey.Read() {
|
2018-10-08 20:06:42 +00:00
|
|
|
s.Close()
|
2018-10-28 05:22:13 +00:00
|
|
|
return nil
|
|
|
|
} else if ev.EnterKey.Read() || ev.EscapeKey.Read() {
|
|
|
|
s.Execute(s.Text)
|
2018-10-08 20:06:42 +00:00
|
|
|
|
2018-10-28 05:22:13 +00:00
|
|
|
// Auto-close the console unless in REPL mode.
|
|
|
|
if !s.Repl {
|
|
|
|
s.Close()
|
2018-07-26 02:38:54 +00:00
|
|
|
}
|
2018-10-28 05:22:13 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
} else if (ev.Up.Now || ev.Down.Now) && len(s.History) > 0 {
|
|
|
|
// Paging through history.
|
|
|
|
if !s.historyPaging {
|
|
|
|
s.historyPaging = true
|
|
|
|
s.historyIndex = len(s.History)
|
2018-07-26 02:38:54 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 05:22:13 +00:00
|
|
|
// Consume the inputs and make convenient variables.
|
|
|
|
ev.Down.Read()
|
|
|
|
isUp := ev.Up.Read()
|
2018-07-26 02:38:54 +00:00
|
|
|
|
2018-10-28 05:22:13 +00:00
|
|
|
// Scroll through the input history.
|
|
|
|
if isUp {
|
|
|
|
s.historyIndex--
|
|
|
|
if s.historyIndex < 0 {
|
|
|
|
s.historyIndex = 0
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s.historyIndex++
|
|
|
|
if s.historyIndex >= len(s.History) {
|
|
|
|
s.historyIndex = len(s.History) - 1
|
|
|
|
}
|
|
|
|
}
|
2018-07-22 03:43:01 +00:00
|
|
|
|
2018-10-28 05:22:13 +00:00
|
|
|
s.Text = s.History[s.historyIndex]
|
|
|
|
|
|
|
|
}
|
2018-07-22 03:43:01 +00:00
|
|
|
|
|
|
|
// Cursor flip?
|
2019-07-05 23:04:36 +00:00
|
|
|
if shmem.Tick > s.cursorFlip {
|
|
|
|
s.cursorFlip = shmem.Tick + s.cursorRate
|
2018-07-26 02:38:54 +00:00
|
|
|
if s.cursor == ' ' {
|
|
|
|
s.cursor = '_'
|
2018-07-22 03:43:01 +00:00
|
|
|
} else {
|
2018-07-26 02:38:54 +00:00
|
|
|
s.cursor = ' '
|
2018-07-22 03:43:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read a character from the keyboard.
|
|
|
|
if key := ev.ReadKey(); key != "" {
|
|
|
|
// Backspace?
|
|
|
|
if key == `\b` {
|
|
|
|
if len(s.Text) > 0 {
|
|
|
|
s.Text = s.Text[:len(s.Text)-1]
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s.Text += key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// How tall is the box?
|
|
|
|
boxHeight := int32(lineHeight*(balance.ShellHistoryLineCount+1)) + balance.ShellPadding
|
|
|
|
|
|
|
|
// Draw the background color.
|
|
|
|
d.Engine.DrawBox(
|
|
|
|
balance.ShellBackgroundColor,
|
|
|
|
render.Rect{
|
|
|
|
X: 0,
|
2018-10-19 20:31:58 +00:00
|
|
|
Y: int32(d.height) - boxHeight,
|
|
|
|
W: int32(d.width),
|
2018-07-22 03:43:01 +00:00
|
|
|
H: boxHeight,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
|
|
|
|
// Draw the recent commands.
|
2018-10-19 20:31:58 +00:00
|
|
|
outputY := int32(d.height - (lineHeight * 2))
|
2018-07-22 03:43:01 +00:00
|
|
|
for i := 0; i < balance.ShellHistoryLineCount; i++ {
|
|
|
|
if len(s.Output) > i {
|
|
|
|
line := s.Output[len(s.Output)-1-i]
|
|
|
|
d.Engine.DrawText(
|
|
|
|
render.Text{
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
FontFilename: balance.ShellFontFilename,
|
|
|
|
Text: line,
|
|
|
|
Size: balance.ShellFontSize,
|
|
|
|
Color: balance.ShellForegroundColor,
|
2018-07-22 03:43:01 +00:00
|
|
|
},
|
|
|
|
render.Point{
|
|
|
|
X: balance.ShellPadding,
|
|
|
|
Y: outputY,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
}
|
|
|
|
outputY -= int32(lineHeight)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw the command prompt.
|
|
|
|
d.Engine.DrawText(
|
|
|
|
render.Text{
|
Menu Toolbar for Editor + Shell Prompts + Theme
* Added a "menu toolbar" to the top of the Edit Mode with useful buttons
that work: New Level, New Doodad (same thing), Save, Save as, Open.
* Added ability for the dev console to prompt the user for a question,
which opens the console automatically. "Save", "Save as" and "Load"
ask for their filenames this way.
* Started groundwork for theming the app. The palette window is a light
brown with an orange title bar, the Menu Toolbar has a black
background, etc.
* Added support for multiple fonts instead of just monospace. DejaVu
Sans (normal and bold) are used now for most labels and window titles,
respectively. The dev console uses DejaVu Sans Mono as before.
* Update ui.Label to accept PadX and PadY separately instead of only
having the Padding option which did both.
* Improvements to Frame packing algorithm.
* Set the SDL draw mode to BLEND so we can use alpha colors properly,
so now the dev console is semi-translucent.
2018-08-12 00:30:00 +00:00
|
|
|
FontFilename: balance.ShellFontFilename,
|
|
|
|
Text: s.Prompt + s.Text + string(s.cursor),
|
|
|
|
Size: balance.ShellFontSize,
|
|
|
|
Color: balance.ShellPromptColor,
|
2018-07-22 03:43:01 +00:00
|
|
|
},
|
|
|
|
render.Point{
|
|
|
|
X: balance.ShellPadding,
|
2018-10-19 20:31:58 +00:00
|
|
|
Y: int32(d.height-balance.ShellFontSize) - balance.ShellPadding,
|
2018-07-22 03:43:01 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
} else if len(s.Flashes) > 0 {
|
|
|
|
// Otherwise, just draw flashed messages.
|
|
|
|
valid := false // Did we actually draw any?
|
|
|
|
|
2019-07-04 03:24:04 +00:00
|
|
|
outputY := int32(d.height - (lineHeight * 2) - 16)
|
2018-07-22 03:43:01 +00:00
|
|
|
for i := len(s.Flashes); i > 0; i-- {
|
|
|
|
flash := s.Flashes[i-1]
|
2019-07-05 23:04:36 +00:00
|
|
|
if shmem.Tick >= flash.Expires {
|
2018-07-22 03:43:01 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
d.Engine.DrawText(
|
|
|
|
render.Text{
|
|
|
|
Text: flash.Text,
|
|
|
|
Size: balance.ShellFontSize,
|
|
|
|
Color: render.SkyBlue,
|
|
|
|
Stroke: render.Grey,
|
|
|
|
Shadow: render.Black,
|
|
|
|
},
|
|
|
|
render.Point{
|
2019-07-04 03:24:04 +00:00
|
|
|
X: balance.ShellPadding + toolbarWidth,
|
2018-07-22 03:43:01 +00:00
|
|
|
Y: outputY,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
outputY -= int32(lineHeight)
|
|
|
|
valid = true
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we've exhausted all flashes, free up the memory.
|
|
|
|
if !valid {
|
|
|
|
s.Flashes = []Flash{}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|