Add JS + History to Shell, Add Main Scene
* The shell now supports an "eval" command, or "$" for short. * Runs it in an Otto JavaScript VM. * Some global variables are available, like `d` is the Doodle object itself, `log`, `RGBA()` and `Point()` * The shell supports paging through input history using the arrow keys. * Added an initial Main Scene
This commit is contained in:
parent
94c1df050b
commit
41e1838549
46
Debug Notes.md
Normal file
46
Debug Notes.md
Normal file
|
@ -0,0 +1,46 @@
|
|||
# Debug Notes
|
||||
|
||||
## Entering Debug Mode
|
||||
|
||||
Command line argument:
|
||||
|
||||
```bash
|
||||
% doodle -debug
|
||||
|
||||
# running the dev build uses debug mode by default
|
||||
% make run
|
||||
```
|
||||
|
||||
In the developer console:
|
||||
|
||||
```dos
|
||||
> boolProp Debug true
|
||||
> boolProp D true
|
||||
```
|
||||
|
||||
## Debug Options
|
||||
|
||||
The `boolProp` command can also be used to toggle on and off different
|
||||
debug options while the game is running.
|
||||
|
||||
```
|
||||
DebugOverlay
|
||||
DO
|
||||
Toggles the main debug text overlay with FPS counter.
|
||||
|
||||
DebugCollision
|
||||
DC
|
||||
Toggles the collision detection bounding box lines.
|
||||
```
|
||||
|
||||
## JavaScript Shell
|
||||
|
||||
The developer console can parse JavaScript commands for more access to the
|
||||
game's internal objects.
|
||||
|
||||
The following global variables are available to the shell:
|
||||
|
||||
* `d` is the master Doodle struct.
|
||||
* `log` is the master logger object for logging messages to the terminal.
|
||||
* `RGBA()` is the `render.RGBA()` function for creating a Color value.
|
||||
* `Point(x, y)` to create a `render.Point`
|
39
README.md
39
README.md
|
@ -147,9 +147,46 @@ As a rough idea of the milestones needed for this game to work:
|
|||
* [ ] UI Manager that will keep track of buttons to know when the mouse
|
||||
is interacting with them.
|
||||
* [ ] Frames
|
||||
* Like Buttons, can have border (raised, sunken or solid), padding and
|
||||
background color.
|
||||
* [ ] Should be able to size themselves dynamically based on child widgets.
|
||||
* [ ] Windows (fixed, non-draggable is OK)
|
||||
* [ ] Expand the Palette support in levels for solid vs. transparent, fire,
|
||||
* [ ] Title bar with label
|
||||
* [ ] Window body implements a Frame that contains child widgets.
|
||||
* [ ] Window can resize itself dynamically based on the Frame.
|
||||
* [ ] Create a "Main Menu" scene with buttons to enter a new Edit Mode,
|
||||
play an existing map from disk, etc.
|
||||
* [ ] Add user interface Frames or Windows to the Edit Mode.
|
||||
* [ ] A toolbar of buttons (New, Save, Open, Play) can be drawn at the top
|
||||
before the UI toolkit gains a proper MenuBar widget.
|
||||
* [ ] Expand the Palette support in levels for solid vs. transparent, fire,
|
||||
etc. with UI toolbar to choose palettes.
|
||||
|
||||
Lesser important UI features that can come at any later time:
|
||||
|
||||
* [ ] MenuBar widget with drop-down menu support.
|
||||
* [ ] Checkbox and Radiobox widgets.
|
||||
* [ ] Text Entry widgets (in the meantime use the Developer Shell to prompt for
|
||||
text input questions)
|
||||
|
||||
## Doodad Editor
|
||||
|
||||
* [ ] The Edit Mode should support creating drawings for Doodads.
|
||||
* [ ] It should know whether you're drawing a Map or a Doodad as some
|
||||
behaviors may need to be different between the two.
|
||||
* [ ] Compress the coordinates down toward `(0,0)` when saving a Doodad,
|
||||
by finding the toppest, leftest point and making that `(0,0)` and adjusting
|
||||
the rest accordingly. This will help trim down Doodads into the smallest
|
||||
possible space for easy collision detection.
|
||||
* [ ] Add a UX to edit multiple frames for a Doodad.
|
||||
* [ ] Edit Mode should be able to fully save the drawings and frames, and an
|
||||
external CLI tool can install the JavaScript into them.
|
||||
* [ ] Possible UX to toggle Doodad options, like its collision rules and
|
||||
whether the Doodad is continued to be "mobile" (i.e. doors and buttons won't
|
||||
move, but items and enemies may be able to; and non-mobile Doodads don't
|
||||
need to collision check against level geometry).
|
||||
* [ ] Edit Mode should have a Doodad Palette (Frame or Window) to drag
|
||||
Doodads into the map.
|
||||
* [ ] ???
|
||||
|
||||
# Building
|
||||
|
|
46
commands.go
46
commands.go
|
@ -3,6 +3,7 @@ package doodle
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Command is a parsed shell command.
|
||||
|
@ -36,6 +37,13 @@ func (c Command) Run(d *Doodle) error {
|
|||
return c.Quit()
|
||||
case "help":
|
||||
return c.Help(d)
|
||||
case "eval":
|
||||
case "$":
|
||||
out, err := d.shell.js.Run(c.ArgsLiteral)
|
||||
d.Flash("%+v", out)
|
||||
return err
|
||||
case "boolProp":
|
||||
return c.BoolProp(d)
|
||||
default:
|
||||
return c.Default()
|
||||
}
|
||||
|
@ -91,7 +99,7 @@ func (c Command) Help(d *Doodle) error {
|
|||
|
||||
// Save the current map to disk.
|
||||
func (c Command) Save(d *Doodle) error {
|
||||
if scene, ok := d.scene.(*EditorScene); ok {
|
||||
if scene, ok := d.Scene.(*EditorScene); ok {
|
||||
filename := ""
|
||||
if len(c.Args) > 0 {
|
||||
filename = c.Args[0]
|
||||
|
@ -139,6 +147,42 @@ func (c Command) Quit() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// BoolProp command sets available boolean variables.
|
||||
func (c Command) BoolProp(d *Doodle) error {
|
||||
if len(c.Args) != 2 {
|
||||
return errors.New("Usage: boolProp <name> <true or false>")
|
||||
}
|
||||
|
||||
var (
|
||||
name = c.Args[0]
|
||||
value = c.Args[1]
|
||||
truthy = value[0] == 't' || value[0] == 'T' || value[0] == '1'
|
||||
ok = true
|
||||
)
|
||||
|
||||
switch name {
|
||||
case "Debug":
|
||||
case "D":
|
||||
d.Debug = truthy
|
||||
case "DebugOverlay":
|
||||
case "DO":
|
||||
DebugOverlay = truthy
|
||||
case "DebugCollision":
|
||||
case "DC":
|
||||
DebugCollision = truthy
|
||||
default:
|
||||
ok = false
|
||||
}
|
||||
|
||||
if ok {
|
||||
d.Flash("Set boolProp %s=%s", name, strconv.FormatBool(truthy))
|
||||
} else {
|
||||
d.Flash("Unknown boolProp name %s", name)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Default command.
|
||||
func (c Command) Default() error {
|
||||
return fmt.Errorf("%s: command not found. Try `help` for help",
|
||||
|
|
12
doodle.go
12
doodle.go
|
@ -32,7 +32,7 @@ type Doodle struct {
|
|||
// Command line shell options.
|
||||
shell Shell
|
||||
|
||||
scene Scene
|
||||
Scene Scene
|
||||
}
|
||||
|
||||
// New initializes the game object.
|
||||
|
@ -62,8 +62,8 @@ func (d *Doodle) Run() error {
|
|||
}
|
||||
|
||||
// Set up the default scene.
|
||||
if d.scene == nil {
|
||||
d.Goto(&EditorScene{})
|
||||
if d.Scene == nil {
|
||||
d.Goto(&MainScene{})
|
||||
}
|
||||
|
||||
log.Info("Enter Main Loop")
|
||||
|
@ -96,14 +96,14 @@ func (d *Doodle) Run() error {
|
|||
}
|
||||
|
||||
// Run the scene's logic.
|
||||
err = d.scene.Loop(d, ev)
|
||||
err = d.Scene.Loop(d, ev)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the scene.
|
||||
d.scene.Draw(d)
|
||||
d.Scene.Draw(d)
|
||||
|
||||
// Draw the shell.
|
||||
err = d.shell.Draw(d, ev)
|
||||
|
@ -114,7 +114,7 @@ func (d *Doodle) Run() error {
|
|||
}
|
||||
|
||||
// Draw the debug overlay over all scenes.
|
||||
// d.DrawDebugOverlay()
|
||||
d.DrawDebugOverlay()
|
||||
|
||||
// Render the pixels to the screen.
|
||||
err = d.Engine.Present()
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
"git.kirsle.net/apps/doodle/events"
|
||||
"git.kirsle.net/apps/doodle/level"
|
||||
"git.kirsle.net/apps/doodle/render"
|
||||
"git.kirsle.net/apps/doodle/ui"
|
||||
)
|
||||
|
||||
// EditorScene manages the "Edit Level" game mode.
|
||||
|
@ -133,59 +132,6 @@ func (s *EditorScene) Loop(d *Doodle, ev *events.State) error {
|
|||
func (s *EditorScene) Draw(d *Doodle) error {
|
||||
s.canvas.Draw(d.Engine)
|
||||
|
||||
label := ui.NewLabel(render.Text{
|
||||
Text: "Hello UI toolkit!",
|
||||
Size: 26,
|
||||
Color: render.Pink,
|
||||
Stroke: render.SkyBlue,
|
||||
Shadow: render.Black,
|
||||
})
|
||||
label.SetPoint(render.NewPoint(128, 128))
|
||||
label.Compute(d.Engine)
|
||||
log.Info("Label rect: %+v", label.Size())
|
||||
log.Info("Label at: %s", label.Point())
|
||||
label.Present(d.Engine)
|
||||
|
||||
button := ui.NewButton(*ui.NewLabel(render.Text{
|
||||
Text: "Hello",
|
||||
Size: 14,
|
||||
Color: render.Black,
|
||||
}))
|
||||
button.SetPoint(render.NewPoint(200, 200))
|
||||
button.Present(d.Engine)
|
||||
|
||||
// Point and size of that button
|
||||
point := button.Point()
|
||||
size := button.Size()
|
||||
|
||||
button2 := ui.NewButton(*ui.NewLabel(render.Text{
|
||||
Text: "World!",
|
||||
Size: 14,
|
||||
Color: render.Blue,
|
||||
}))
|
||||
button2.SetPoint(render.Point{
|
||||
X: point.X + size.W,
|
||||
Y: point.Y,
|
||||
})
|
||||
button2.Present(d.Engine)
|
||||
|
||||
button.SetText("Buttons that don't click yet")
|
||||
button.SetPoint(render.NewPoint(250, 300))
|
||||
button.Label.Text.Size = 24
|
||||
button.Border = 8
|
||||
button.Outline = 4
|
||||
button.Present(d.Engine)
|
||||
|
||||
button2.SetText("Multiple colors, too")
|
||||
button2.Label.Text.Color = render.White
|
||||
button2.Background = render.RGBA(0, 153, 255, 255)
|
||||
button2.HighlightColor = render.RGBA(100, 200, 255, 255)
|
||||
button2.ShadowColor = render.RGBA(0, 100, 153, 255)
|
||||
button2.SetPoint(render.NewPoint(10, 300))
|
||||
button2.Present(d.Engine)
|
||||
|
||||
_ = label
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
22
fps.go
22
fps.go
|
@ -10,6 +10,13 @@ import (
|
|||
// Frames to cache for FPS calculation.
|
||||
const maxSamples = 100
|
||||
|
||||
// Debug mode options, these can be enabled in the dev console
|
||||
// like: boolProp DebugOverlay true
|
||||
var (
|
||||
DebugOverlay = true
|
||||
DebugCollision = true
|
||||
)
|
||||
|
||||
var (
|
||||
fpsCurrentTicks uint32 // current time we get sdl.GetTicks()
|
||||
fpsLastTime uint32 // last time we printed the fpsCurrentTicks
|
||||
|
@ -21,7 +28,7 @@ var (
|
|||
|
||||
// DrawDebugOverlay draws the debug FPS text on the SDL canvas.
|
||||
func (d *Doodle) DrawDebugOverlay() {
|
||||
if !d.Debug {
|
||||
if !d.Debug || !DebugOverlay {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -29,7 +36,7 @@ func (d *Doodle) DrawDebugOverlay() {
|
|||
"FPS: %d (%dms) S:%s F12=screenshot",
|
||||
fpsCurrent,
|
||||
fpsSkipped,
|
||||
d.scene.Name(),
|
||||
d.Scene.Name(),
|
||||
)
|
||||
|
||||
err := d.Engine.DrawText(
|
||||
|
@ -52,6 +59,10 @@ func (d *Doodle) DrawDebugOverlay() {
|
|||
|
||||
// DrawCollisionBox draws the collision box around a Doodad.
|
||||
func (d *Doodle) DrawCollisionBox(actor doodads.Doodad) {
|
||||
if !d.Debug || !DebugCollision {
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
rect = doodads.GetBoundingRect(actor)
|
||||
box = doodads.GetCollisionBox(rect)
|
||||
|
@ -74,13 +85,6 @@ func (d *Doodle) TrackFPS(skipped uint32) {
|
|||
}
|
||||
|
||||
if fpsLastTime < fpsCurrentTicks-fpsInterval {
|
||||
// log.Debug("Uptime: %s FPS: %d deltaTicks: %d skipped: %dms",
|
||||
// time.Now().Sub(d.startTime),
|
||||
// fpsCurrent,
|
||||
// fpsCurrentTicks-fpsLastTime,
|
||||
// skipped,
|
||||
// )
|
||||
|
||||
fpsLastTime = fpsCurrentTicks
|
||||
fpsCurrent = fpsFrames
|
||||
fpsFrames = 0
|
||||
|
|
74
main_scene.go
Normal file
74
main_scene.go
Normal file
|
@ -0,0 +1,74 @@
|
|||
package doodle
|
||||
|
||||
import (
|
||||
"git.kirsle.net/apps/doodle/events"
|
||||
"git.kirsle.net/apps/doodle/render"
|
||||
"git.kirsle.net/apps/doodle/ui"
|
||||
)
|
||||
|
||||
// MainScene implements the main menu of Doodle.
|
||||
type MainScene struct {
|
||||
}
|
||||
|
||||
// Name of the scene.
|
||||
func (s *MainScene) Name() string {
|
||||
return "Main"
|
||||
}
|
||||
|
||||
// Setup the scene.
|
||||
func (s *MainScene) Setup(d *Doodle) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Loop the editor scene.
|
||||
func (s *MainScene) Loop(d *Doodle, ev *events.State) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Draw the pixels on this frame.
|
||||
func (s *MainScene) Draw(d *Doodle) error {
|
||||
// Clear the canvas and fill it with white.
|
||||
d.Engine.Clear(render.White)
|
||||
|
||||
label := ui.NewLabel(render.Text{
|
||||
Text: "Doodle v" + Version,
|
||||
Size: 26,
|
||||
Color: render.Pink,
|
||||
Stroke: render.SkyBlue,
|
||||
Shadow: render.Black,
|
||||
})
|
||||
label.Compute(d.Engine)
|
||||
label.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (label.Size().W / 2),
|
||||
Y: 120,
|
||||
})
|
||||
label.Present(d.Engine)
|
||||
|
||||
button := ui.NewButton(*ui.NewLabel(render.Text{
|
||||
Text: "New Map",
|
||||
Size: 14,
|
||||
Color: render.Black,
|
||||
}))
|
||||
button.Compute(d.Engine)
|
||||
|
||||
button.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (button.Size().W / 2),
|
||||
Y: 200,
|
||||
})
|
||||
button.Present(d.Engine)
|
||||
|
||||
button.SetText("Load Map")
|
||||
button.Compute(d.Engine)
|
||||
button.MoveTo(render.Point{
|
||||
X: (d.width / 2) - (button.Size().W / 2),
|
||||
Y: 260,
|
||||
})
|
||||
button.Present(d.Engine)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy the scene.
|
||||
func (s *MainScene) Destroy() error {
|
||||
return nil
|
||||
}
|
|
@ -21,7 +21,7 @@ type PlayScene struct {
|
|||
height int32
|
||||
|
||||
// Player character
|
||||
player doodads.Doodad
|
||||
Player doodads.Doodad
|
||||
}
|
||||
|
||||
// Name of the scene.
|
||||
|
@ -42,7 +42,7 @@ func (s *PlayScene) Setup(d *Doodle) error {
|
|||
s.Filename = ""
|
||||
}
|
||||
|
||||
s.player = doodads.NewPlayer()
|
||||
s.Player = doodads.NewPlayer()
|
||||
|
||||
if s.canvas == nil {
|
||||
log.Debug("PlayScene.Setup: no grid given, initializing empty grid")
|
||||
|
@ -80,17 +80,17 @@ func (s *PlayScene) Draw(d *Doodle) error {
|
|||
s.canvas.Draw(d.Engine)
|
||||
|
||||
// Draw our hero.
|
||||
s.player.Draw(d.Engine)
|
||||
s.Player.Draw(d.Engine)
|
||||
|
||||
// Draw out bounding boxes.
|
||||
d.DrawCollisionBox(s.player)
|
||||
d.DrawCollisionBox(s.Player)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// movePlayer updates the player's X,Y coordinate based on key pressed.
|
||||
func (s *PlayScene) movePlayer(ev *events.State) {
|
||||
delta := s.player.Position()
|
||||
delta := s.Player.Position()
|
||||
var playerSpeed int32 = 8
|
||||
var gravity int32 = 2
|
||||
|
||||
|
@ -110,20 +110,20 @@ func (s *PlayScene) movePlayer(ev *events.State) {
|
|||
// Apply gravity.
|
||||
// var onFloor bool
|
||||
|
||||
info, ok := doodads.CollidesWithGrid(s.player, &s.canvas, delta)
|
||||
info, ok := doodads.CollidesWithGrid(s.Player, &s.canvas, delta)
|
||||
if ok {
|
||||
// Collision happened with world.
|
||||
}
|
||||
delta = info.MoveTo
|
||||
|
||||
// Apply gravity if not grounded.
|
||||
if !s.player.Grounded() {
|
||||
if !s.Player.Grounded() {
|
||||
// Gravity has to pipe through the collision checker, too, so it
|
||||
// can't give us a cheated downward boost.
|
||||
delta.Y += gravity
|
||||
}
|
||||
|
||||
s.player.MoveTo(delta)
|
||||
s.Player.MoveTo(delta)
|
||||
}
|
||||
|
||||
// LoadLevel loads a level from disk.
|
||||
|
|
10
scene.go
10
scene.go
|
@ -21,11 +21,11 @@ type Scene interface {
|
|||
// Goto a scene. First it unloads the current scene.
|
||||
func (d *Doodle) Goto(scene Scene) error {
|
||||
// Teardown existing scene.
|
||||
if d.scene != nil {
|
||||
d.scene.Destroy()
|
||||
if d.Scene != nil {
|
||||
d.Scene.Destroy()
|
||||
}
|
||||
|
||||
log.Info("Goto Scene")
|
||||
d.scene = scene
|
||||
return d.scene.Setup(d)
|
||||
log.Info("Goto Scene: %s", scene.Name())
|
||||
d.Scene = scene
|
||||
return d.Scene.Setup(d)
|
||||
}
|
||||
|
|
79
shell.go
79
shell.go
|
@ -8,6 +8,7 @@ import (
|
|||
"git.kirsle.net/apps/doodle/balance"
|
||||
"git.kirsle.net/apps/doodle/events"
|
||||
"git.kirsle.net/apps/doodle/render"
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
// Flash a message to the user.
|
||||
|
@ -18,15 +19,25 @@ func (d *Doodle) Flash(template string, v ...interface{}) {
|
|||
// Shell implements the developer console in-game.
|
||||
type Shell struct {
|
||||
parent *Doodle
|
||||
|
||||
Open bool
|
||||
Prompt string
|
||||
Text string
|
||||
History []string
|
||||
Output []string
|
||||
Flashes []Flash
|
||||
Cursor string
|
||||
|
||||
// Blinky cursor variables.
|
||||
cursor byte // cursor symbol
|
||||
cursorFlip uint64 // ticks until cursor flip
|
||||
cursorRate uint64
|
||||
|
||||
// Paging through history variables.
|
||||
historyPaging bool
|
||||
historyIndex int
|
||||
|
||||
// JavaScript shell interpreter.
|
||||
js *otto.Otto
|
||||
}
|
||||
|
||||
// Flash holds a message to flash on screen.
|
||||
|
@ -37,15 +48,32 @@ type Flash struct {
|
|||
|
||||
// NewShell initializes the shell helper (the "Shellper").
|
||||
func NewShell(d *Doodle) Shell {
|
||||
return Shell{
|
||||
s := Shell{
|
||||
parent: d,
|
||||
History: []string{},
|
||||
Output: []string{},
|
||||
Flashes: []Flash{},
|
||||
Prompt: ">",
|
||||
Cursor: "_",
|
||||
cursor: '_',
|
||||
cursorRate: balance.ShellCursorBlinkRate,
|
||||
js: otto.New(),
|
||||
}
|
||||
|
||||
// Make the Doodle instance available to the shell.
|
||||
bindings := map[string]interface{}{
|
||||
"d": d,
|
||||
"log": log,
|
||||
"RGBA": render.RGBA,
|
||||
"Point": render.NewPoint,
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
// Close the shell, resetting its internal state.
|
||||
|
@ -54,11 +82,18 @@ func (s *Shell) Close() {
|
|||
s.Open = false
|
||||
s.Prompt = ">"
|
||||
s.Text = ""
|
||||
s.historyPaging = false
|
||||
s.historyIndex = 0
|
||||
}
|
||||
|
||||
// Execute a command in the shell.
|
||||
func (s *Shell) Execute(input string) {
|
||||
command := s.Parse(input)
|
||||
if command.Raw != "" {
|
||||
s.Output = append(s.Output, s.Prompt+command.Raw)
|
||||
s.History = append(s.History, command.Raw)
|
||||
}
|
||||
|
||||
if command.Command == "clear" {
|
||||
s.Output = []string{}
|
||||
} else {
|
||||
|
@ -68,10 +103,6 @@ func (s *Shell) Execute(input string) {
|
|||
}
|
||||
}
|
||||
|
||||
if command.Raw != "" {
|
||||
s.History = append(s.History, command.Raw)
|
||||
}
|
||||
|
||||
// Reset the text buffer in the shell.
|
||||
s.Text = ""
|
||||
}
|
||||
|
@ -149,6 +180,32 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
|||
s.Execute(s.Text)
|
||||
s.Close()
|
||||
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)
|
||||
}
|
||||
|
||||
// Consume the inputs and make convenient variables.
|
||||
ev.Down.Read()
|
||||
isUp := ev.Up.Read()
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
s.Text = s.History[s.historyIndex]
|
||||
|
||||
}
|
||||
|
||||
// Compute the line height we can draw.
|
||||
|
@ -159,10 +216,10 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
|||
// Cursor flip?
|
||||
if d.ticks > s.cursorFlip {
|
||||
s.cursorFlip = d.ticks + s.cursorRate
|
||||
if s.Cursor == "" {
|
||||
s.Cursor = "_"
|
||||
if s.cursor == ' ' {
|
||||
s.cursor = '_'
|
||||
} else {
|
||||
s.Cursor = ""
|
||||
s.cursor = ' '
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +272,7 @@ func (s *Shell) Draw(d *Doodle, ev *events.State) error {
|
|||
// Draw the command prompt.
|
||||
d.Engine.DrawText(
|
||||
render.Text{
|
||||
Text: s.Prompt + s.Text + s.Cursor,
|
||||
Text: s.Prompt + s.Text + string(s.cursor),
|
||||
Size: balance.ShellFontSize,
|
||||
Color: balance.ShellForegroundColor,
|
||||
},
|
||||
|
|
|
@ -90,7 +90,7 @@ func (w *Button) Present(e render.Engine) {
|
|||
e.DrawBox(w.Background, box)
|
||||
|
||||
// Draw the text label inside.
|
||||
w.Label.SetPoint(render.Point{
|
||||
w.Label.MoveTo(render.Point{
|
||||
X: P.X + w.Padding + w.Border + w.Outline,
|
||||
Y: P.Y + w.Padding + w.Border + w.Outline,
|
||||
})
|
||||
|
|
13
ui/widget.go
13
ui/widget.go
|
@ -9,7 +9,8 @@ type Widget interface {
|
|||
SetWidth(int32) // Set
|
||||
SetHeight(int32) // Set
|
||||
Point() render.Point
|
||||
SetPoint(render.Point)
|
||||
MoveTo(render.Point)
|
||||
MoveBy(render.Point)
|
||||
Size() render.Rect // Return the Width and Height of the widget.
|
||||
Resize(render.Rect)
|
||||
|
||||
|
@ -35,11 +36,17 @@ func (w *BaseWidget) Point() render.Point {
|
|||
return w.point
|
||||
}
|
||||
|
||||
// SetPoint updates the X,Y position of the widget relative to the window.
|
||||
func (w *BaseWidget) SetPoint(v render.Point) {
|
||||
// MoveTo updates the X,Y position to the new point.
|
||||
func (w *BaseWidget) MoveTo(v render.Point) {
|
||||
w.point = v
|
||||
}
|
||||
|
||||
// MoveBy adds the X,Y values to the widget's current position.
|
||||
func (w *BaseWidget) MoveBy(v render.Point) {
|
||||
w.point.X += v.X
|
||||
w.point.Y += v.Y
|
||||
}
|
||||
|
||||
// Size returns the box with W and H attributes containing the size of the
|
||||
// widget. The X,Y attributes of the box are ignored and zero.
|
||||
func (w *BaseWidget) Size() render.Rect {
|
||||
|
|
Loading…
Reference in New Issue
Block a user