Skip to content

Commit

Permalink
Add cursor overlay on our grid
Browse files Browse the repository at this point in the history
Required updating of underlying TextGrid
Also update bell to change yellow cursor red
  • Loading branch information
andydotxyz committed Feb 29, 2020
1 parent 1ed3185 commit 2be9431
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 29 deletions.
6 changes: 4 additions & 2 deletions escape.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ func (t *Terminal) handleEscape(code string) {
case "2J":
t.clearScreen()
case "K":
t.content.SetText("")
// TODO clear from the cursor to end line
row := t.content.Row(t.cursorRow)
t.content.SetRow(t.cursorRow, row[:t.cursorCol])
default:
log.Println("Unrecognised Escape:", code)
}
}

func (t *Terminal) clearScreen() {
t.content.SetText("")
t.cursorCol = 0
t.cursorRow = 0
}
9 changes: 9 additions & 0 deletions escape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,12 @@ func TestClearScreen(t *testing.T) {
term.handleEscape("2J")
assert.Equal(t, "", term.content.Text())
}

func TestEraseLine(t *testing.T) {
term := NewTerminal()
term.content.SetText("Hello")
assert.Equal(t, "Hello", term.content.Text())

term.handleEscape("K")
assert.Equal(t, "", term.content.Text())
}
72 changes: 51 additions & 21 deletions output.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package terminal

import (
"strings"
"time"
)

func (t *Terminal) handleOutput(buf []byte) {
out := ""
esc := -5
osc := false
code := ""
for i, r := range buf {
for i, r := range []rune(string(buf)) {
if r == asciiEscape {
esc = i
continue
Expand All @@ -18,15 +18,21 @@ func (t *Terminal) handleOutput(buf []byte) {
if r == '[' {
continue
} else if r == ']' {
// TODO only up to BEL or ST
t.handleOSC(string(buf[2 : len(buf)-1]))
break
osc = true
continue
} else {
esc = -5
}
}
if esc != -5 {
if (r >= '0' && r <= '9') || r == ';' || r == '=' {
if osc {
if r == asciiBell || r == 0 {
t.handleOSC(out)
out = ""
osc = false
continue
}
} else if esc != -5 {
if (r >= '0' && r <= '9') || r == ';' || r == '=' || r == '?' {
code += string(r)
} else {
code += string(r)
Expand All @@ -40,16 +46,36 @@ func (t *Terminal) handleOutput(buf []byte) {

switch r {
case asciiBackspace:
runes := []rune(t.content.Text())
if len(runes) == 0 {
row := t.content.Row(t.cursorRow)
if len(row) == 0 {
continue
}
t.content.SetText(string(runes[:len(runes)-1]))
t.content.SetRow(t.cursorRow, row[:len(row)-1])
t.cursorCol--
t.cursorMoved()
continue
case '\n':
row := t.content.Row(t.cursorRow)
row = append(row, []rune(out)...)
t.content.SetRow(t.cursorRow, row)

if t.cursorRow == int(t.config.Rows-1) {
for i = 0; i < t.cursorRow; i++ {
t.content.SetRow(i, t.content.Row(i+1))
}
t.content.SetRow(i, []rune{})
} else {
t.cursorRow++
}

out = ""
t.cursorCol = 0
continue
case '\r':
t.cursorCol = 0
continue
case asciiBell:
go t.bell()
go t.ringBell()
continue
case '\t': // TODO remove silly approximation
out += " "
Expand All @@ -59,21 +85,25 @@ func (t *Terminal) handleOutput(buf []byte) {
esc = -5
code = ""
}
t.content.SetText(t.content.Text() + out)
}

func (t *Terminal) bell() {
add := "*BELL* "
title := t.config.Title
if strings.Index(title, add) == 0 { // don't ring twice at once
if osc {
t.handleOSC(out)
return
}
row := t.content.Row(t.cursorRow)
row = append(row, []rune(out)...)
t.content.SetRow(t.cursorRow, row)
t.cursorCol += len(out)
t.Refresh()
}

func (t *Terminal) ringBell() {
t.bell = true
t.Refresh()

t.config.Title = add + title
t.onConfigure()
select {
case <-time.After(time.Millisecond * 300):
t.config.Title = title
t.onConfigure()
t.bell = false
t.Refresh()
}
}
36 changes: 33 additions & 3 deletions render.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ import (
"image/color"

"fyne.io/fyne"
"fyne.io/fyne/canvas"
)

var (
cursorColor = color.RGBA{R: 255, G: 255, B: 0, A: 128}
cursorBellColor = color.RGBA{R: 255, G: 0, B: 0, A: 128}
)

type render struct {
term *Terminal
term *Terminal
cursor *canvas.Rectangle
}

func (r *render) Layout(s fyne.Size) {
Expand All @@ -19,6 +26,9 @@ func (r *render) MinSize() fyne.Size {
}

func (r *render) Refresh() {
r.moveCursor()
r.refreshCursor()

r.term.content.Refresh()
}

Expand All @@ -27,13 +37,33 @@ func (r *render) BackgroundColor() color.Color {
}

func (r *render) Objects() []fyne.CanvasObject {
return []fyne.CanvasObject{r.term.content}
return []fyne.CanvasObject{r.term.content, r.cursor}
}

func (r *render) Destroy() {
}

func (r *render) moveCursor() {
cell := r.term.guessCellSize()
r.cursor.Move(fyne.NewPos(cell.Width*r.term.cursorCol, cell.Height*r.term.cursorRow))
}

func (r *render) refreshCursor() {
r.cursor.Hidden = !r.term.focused
if r.term.bell {
r.cursor.FillColor = cursorBellColor
} else {
r.cursor.FillColor = cursorColor
}
r.cursor.Refresh()
}

// CreateRenderer requests a new renderer for this terminal (just a wrapper around the TextGrid)
func (t *Terminal) CreateRenderer() fyne.WidgetRenderer {
return &render{term: t}
cur := canvas.NewRectangle(cursorColor)
cur.Resize(fyne.NewSize(2, t.guessCellSize().Height))

r := &render{term: t, cursor: cur}
t.cursorMoved = r.moveCursor
return r
}
8 changes: 5 additions & 3 deletions term.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ type Terminal struct {
listenerLock sync.Mutex
listeners []chan Config

pty *os.File
focused bool
pty *os.File
focused, bell bool
cursorRow, cursorCol int
cursorMoved func()
}

// AddListener registers a new outgoing channel that will have our Config sent each time it changes.
Expand Down Expand Up @@ -138,7 +140,7 @@ func (t *Terminal) Run() error {
func NewTerminal() *Terminal {
t := &Terminal{}
t.ExtendBaseWidget(t)
t.content = widget.NewTextGrid("")
t.content = widget.NewTextGrid()

return t
}

0 comments on commit 2be9431

Please sign in to comment.