1
1
mirror of https://github.com/walles/moar.git synced 2024-11-27 01:05:23 +03:00
moar/m/pager.go

142 lines
2.7 KiB
Go
Raw Normal View History

package m
import (
"fmt"
"os"
"github.com/gdamore/tcell"
)
// Pager is the main on-screen pager
type _Pager struct {
reader _Reader
screen tcell.Screen
quit chan struct{}
firstLineOneBased int
}
// NewPager creates a new Pager
func NewPager(r _Reader) *_Pager {
return &_Pager{
reader: r,
quit: make(chan struct{}),
firstLineOneBased: 1,
}
}
2019-06-12 08:07:13 +03:00
func (p *_Pager) _AddLine(lineNumber int, line string) {
for pos, char := range line {
p.screen.SetContent(pos, lineNumber, char, nil, tcell.StyleDefault)
}
}
func (p *_Pager) _AddLines() {
2019-06-11 22:09:57 +03:00
_, height := p.screen.Size()
wantedLineCount := height - 1
2019-06-11 19:52:38 +03:00
lines := p.reader.GetLines(p.firstLineOneBased, wantedLineCount)
2019-06-12 08:07:13 +03:00
// If we're asking for past-the-end lines, the Reader will clip for us,
// and we should adapt to that. Otherwise if you scroll 100 lines past
// the end, you'll then have to scroll 100 lines up again before the
// display starts scrolling visibly.
p.firstLineOneBased = lines.firstLineOneBased
for screenLineNumber, line := range lines.lines {
p._AddLine(screenLineNumber, line)
2019-06-11 19:52:38 +03:00
}
}
2019-06-12 08:07:13 +03:00
func (p *_Pager) _AddFooter() {
_, height := p.screen.Size()
p._AddLine(height-1, "Press ESC / Return / q to exit")
}
2019-06-11 22:09:57 +03:00
func (p *_Pager) _Redraw() {
p.screen.Clear()
2019-06-11 19:52:38 +03:00
2019-06-12 08:07:13 +03:00
p._AddLines()
2019-06-11 19:52:38 +03:00
2019-06-11 22:09:57 +03:00
p._AddFooter()
p.screen.Sync()
2019-06-11 19:52:38 +03:00
}
2019-06-11 22:21:12 +03:00
func (p *_Pager) _Quit() {
close(p.quit)
}
func (p *_Pager) _OnKey(key tcell.Key) {
switch key {
case tcell.KeyEscape, tcell.KeyEnter:
p._Quit()
case tcell.KeyUp:
// Clipping is done in _AddLines()
p.firstLineOneBased--
case tcell.KeyDown:
// Clipping is done in _AddLines()
p.firstLineOneBased++
2019-06-13 07:14:41 +03:00
case tcell.KeyHome:
p.firstLineOneBased = 1
case tcell.KeyEnd:
p.firstLineOneBased = p.reader.LineCount() + 1
}
}
2019-06-11 22:32:24 +03:00
func (p *_Pager) _OnRune(char rune) {
switch char {
case 'q':
p._Quit()
2019-06-13 07:14:41 +03:00
case '<', 'g':
p.firstLineOneBased = 1
case '>', 'G':
p.firstLineOneBased = p.reader.LineCount() + 1
2019-06-11 22:32:24 +03:00
}
}
// StartPaging brings up the pager on screen
func (p *_Pager) StartPaging() {
// This function initially inspired by
// https://github.com/gdamore/tcell/blob/master/_demos/unicode.go
s, e := tcell.NewScreen()
if e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
if e = s.Init(); e != nil {
fmt.Fprintf(os.Stderr, "%v\n", e)
os.Exit(1)
}
2019-06-11 22:22:32 +03:00
defer s.Fini()
2019-06-11 22:21:12 +03:00
p.screen = s
// Main loop
go func() {
2019-06-11 19:52:38 +03:00
s.Show()
for {
2019-06-11 22:09:57 +03:00
p._Redraw()
2019-06-11 19:52:38 +03:00
ev := s.PollEvent()
switch ev := ev.(type) {
case *tcell.EventKey:
2019-06-11 22:32:24 +03:00
if ev.Key() == tcell.KeyRune {
p._OnRune(ev.Rune())
} else {
p._OnKey(ev.Key())
}
case *tcell.EventResize:
// We'll be implicitly redrawn just by taking another lap in the loop
}
}
}()
2019-06-11 22:21:12 +03:00
<-p.quit
}