Fix a bug in macOS terminal where stuff could get sucked into the scroll area

This commit is contained in:
Christian Rocha 2020-07-13 11:39:04 -04:00
parent 8025748828
commit 2f53eeb54b
No known key found for this signature in database
GPG Key ID: D6CC7A16E5878018
2 changed files with 37 additions and 13 deletions

View File

@ -29,6 +29,9 @@ type renderer struct {
lastRender string
linesRendered int
// essentially whether or not we're using the full size of the terminal
altScreenActive bool
// renderer dimensions; usually the size of the window
width int
height int
@ -156,7 +159,14 @@ func (r *renderer) flush() {
// Make sure the cursor is at the start of the last line to keep rendering
// behavior consistent.
cursorBack(out, r.width)
if r.altScreenActive {
// We need this case to fix a bug in macOS terminal. In other terminals
// the below case seems to do the job regardless of whether or not we're
// using the full terminal window.
moveCursor(out, r.linesRendered, 0)
} else {
cursorBack(out, r.width)
}
_, _ = r.out.Write(out.Bytes())
r.lastRender = r.buf.String()

38
tea.go
View File

@ -53,8 +53,10 @@ type Program struct {
update Update
view View
mtx sync.Mutex
output *os.File // where to send output. this will usually be os.Stdout.
mtx sync.Mutex
output *os.File // where to send output. this will usually be os.Stdout.
renderer *renderer
altScreenActive bool
}
// Quit is a special command that tells the program to exit.
@ -91,13 +93,14 @@ func NewProgram(init Init, update Update, view View) *Program {
// Start initializes the program.
func (p *Program) Start() error {
var (
cmds = make(chan Cmd)
msgs = make(chan Msg)
errs = make(chan error)
done = make(chan struct{})
mrRenderer = newRenderer(p.output, &p.mtx)
cmds = make(chan Cmd)
msgs = make(chan Msg)
errs = make(chan error)
done = make(chan struct{})
)
p.renderer = newRenderer(p.output, &p.mtx)
err := initTerminal()
if err != nil {
return err
@ -113,10 +116,11 @@ func (p *Program) Start() error {
}
// Start renderer
mrRenderer.start()
p.renderer.start()
p.renderer.altScreenActive = p.altScreenActive
// Render initial view
mrRenderer.write(p.view(model))
p.renderer.write(p.view(model))
// Subscribe to user input
go func() {
@ -167,7 +171,7 @@ func (p *Program) Start() error {
// Handle quit message
if _, ok := msg.(quitMsg); ok {
mrRenderer.stop()
p.renderer.stop()
close(done)
return nil
}
@ -181,11 +185,11 @@ func (p *Program) Start() error {
}
// Process internal messages for the renderer
mrRenderer.handleMessages(msg)
p.renderer.handleMessages(msg)
var cmd Cmd
model, cmd = p.update(msg, model) // run update
cmds <- cmd // process command (if any)
mrRenderer.write(p.view(model)) // send view to renderer
p.renderer.write(p.view(model)) // send view to renderer
}
}
}
@ -196,6 +200,11 @@ func (p *Program) EnterAltScreen() {
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.AltScreenSeq)
moveCursor(p.output, 0, 0)
p.altScreenActive = true
if p.renderer != nil {
p.renderer.altScreenActive = p.altScreenActive
}
}
// ExitAltScreen exits the alternate screen buffer.
@ -203,6 +212,11 @@ func (p *Program) ExitAltScreen() {
p.mtx.Lock()
defer p.mtx.Unlock()
fmt.Fprintf(p.output, te.CSI+te.ExitAltScreenSeq)
p.altScreenActive = false
if p.renderer != nil {
p.renderer.altScreenActive = p.altScreenActive
}
}
// EnableMouseCellMotion enables mouse click, release, wheel and motion events if a