ui: remove screen and viewports

Remove references to tcell.Screen or views.Viewports. Convert Contexts
and the core UI struct to use Vaxis objects only.

Signed-off-by: Tim Culverhouse <tim@timculverhouse.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
Tim Culverhouse
2024-02-12 06:26:16 -06:00
committed by Robin Jarry
parent 63b9706441
commit 787cfbd9a9
5 changed files with 78 additions and 79 deletions

View File

@@ -83,11 +83,11 @@ func (term *Terminal) Invalidate() {
}
func (term *Terminal) Draw(ctx *ui.Context) {
term.vterm.SetSurface(ctx.View())
term.vterm.SetSurface(ctx)
w, h := ctx.View().Size()
w, h := ctx.Size()
if !term.isClosed() && term.ctx != nil {
ow, oh := term.ctx.View().Size()
ow, oh := term.ctx.Size()
if w != ow || h != oh {
term.vterm.Resize(w, h)
}
@@ -109,8 +109,7 @@ func (term *Terminal) Draw(ctx *ui.Context) {
if term.focus {
y, x, style, vis := term.vterm.Cursor()
if vis && !term.isClosed() {
ctx.SetCursor(x, y)
ctx.SetCursorStyle(style)
ctx.SetCursor(x, y, vaxis.CursorStyle(style))
} else {
ctx.HideCursor()
}
@@ -150,8 +149,7 @@ func (term *Terminal) Focus(focus bool) {
term.ctx.HideCursor()
} else {
y, x, style, _ := term.vterm.Cursor()
term.ctx.SetCursor(x, y)
term.ctx.SetCursorStyle(style)
term.ctx.SetCursor(x, y, vaxis.CursorStyle(style))
term.Invalidate()
}
}

View File

@@ -6,35 +6,22 @@ import (
"git.sr.ht/~rjarry/aerc/lib/parse"
"git.sr.ht/~rockorager/vaxis"
"github.com/gdamore/tcell/v2"
"github.com/gdamore/tcell/v2/views"
)
// A context allows you to draw in a sub-region of the terminal
type Context struct {
screen tcell.Screen
viewport *views.ViewPort
window vaxis.Window
x, y int
onPopover func(*Popover)
}
func (ctx *Context) X() int {
x, _, _, _ := ctx.viewport.GetPhysical()
return x
}
func (ctx *Context) Y() int {
_, y, _, _ := ctx.viewport.GetPhysical()
return y
}
func (ctx *Context) Width() int {
width, _ := ctx.viewport.Size()
width, _ := ctx.window.Size()
return width
}
func (ctx *Context) Height() int {
_, height := ctx.viewport.Size()
_, height := ctx.window.Size()
return height
}
@@ -43,39 +30,37 @@ func (ctx *Context) Window() vaxis.Window {
return ctx.window
}
func NewContext(width, height int, screen tcell.Screen, p func(*Popover)) *Context {
vp := views.NewViewPort(screen, 0, 0, width, height)
win := screen.Vaxis().Window()
return &Context{screen, vp, win, 0, 0, p}
func NewContext(width, height int, vx *vaxis.Vaxis, p func(*Popover)) *Context {
win := vx.Window()
return &Context{win, 0, 0, p}
}
func (ctx *Context) Subcontext(x, y, width, height int) *Context {
vp_width, vp_height := ctx.viewport.Size()
if x < 0 || y < 0 {
panic(fmt.Errorf("Attempted to create context with negative offset"))
}
if x+width > vp_width || y+height > vp_height {
panic(fmt.Errorf("Attempted to create context larger than parent"))
}
vp := views.NewViewPort(ctx.viewport, x, y, width, height)
win := ctx.window.New(x, y, width, height)
return &Context{ctx.screen, vp, win, ctx.x + x, ctx.y + y, ctx.onPopover}
return &Context{win, x, y, ctx.onPopover}
}
func (ctx *Context) SetCell(x, y int, ch rune, style tcell.Style) {
width, height := ctx.viewport.Size()
width, height := ctx.window.Size()
if x >= width || y >= height {
// no-op when dims are inadequate
return
}
crunes := []rune{}
ctx.viewport.SetContent(x, y, ch, crunes, style)
ctx.window.SetCell(x, y, vaxis.Cell{
Character: vaxis.Character{
Grapheme: string(ch),
},
Style: tcell.VaxisStyle(style),
})
}
func (ctx *Context) Printf(x, y int, style tcell.Style,
format string, a ...interface{},
) int {
width, height := ctx.viewport.Size()
width, height := ctx.window.Size()
if x >= width || y >= height {
// no-op when dims are inadequate
@@ -103,8 +88,13 @@ func (ctx *Context) Printf(x, y int, style tcell.Style,
case '\r':
x = old_x
default:
crunes := []rune{}
ctx.viewport.SetContent(x, y, sr.Value, crunes, sr.Style)
ctx.window.SetCell(x, y, vaxis.Cell{
Character: vaxis.Character{
Grapheme: string(sr.Value),
Width: sr.Width,
},
Style: tcell.VaxisStyle(sr.Style),
})
x += sr.Width
if x == old_x+width {
if !newline() {
@@ -118,20 +108,22 @@ func (ctx *Context) Printf(x, y int, style tcell.Style,
}
func (ctx *Context) Fill(x, y, width, height int, rune rune, style tcell.Style) {
vp := views.NewViewPort(ctx.viewport, x, y, width, height)
vp.Fill(rune, style)
win := ctx.window.New(x, y, width, height)
win.Fill(vaxis.Cell{
Character: vaxis.Character{
Grapheme: string(rune),
Width: 1,
},
Style: tcell.VaxisStyle(style),
})
}
func (ctx *Context) SetCursor(x, y int) {
ctx.screen.ShowCursor(ctx.x+x, ctx.y+y)
}
func (ctx *Context) SetCursorStyle(cs tcell.CursorStyle) {
ctx.screen.SetCursorStyle(cs)
func (ctx *Context) SetCursor(x, y int, style vaxis.CursorStyle) {
ctx.window.ShowCursor(x, y, style)
}
func (ctx *Context) HideCursor() {
ctx.screen.HideCursor()
ctx.window.Vx.HideCursor()
}
func (ctx *Context) Popover(x, y, width, height int, d Drawable) {
@@ -144,10 +136,19 @@ func (ctx *Context) Popover(x, y, width, height int, d Drawable) {
})
}
func (ctx *Context) View() *views.ViewPort {
return ctx.viewport
// SetContent is used to update the content of the Surface at the given
// location.
func (ctx *Context) SetContent(x int, y int, ch rune, comb []rune, style tcell.Style) {
g := []rune{ch}
g = append(g, comb...)
ctx.window.SetCell(x, y, vaxis.Cell{
Character: vaxis.Character{
Grapheme: string(g),
},
Style: tcell.VaxisStyle(style),
})
}
func (ctx *Context) Show() {
ctx.screen.Show()
func (ctx *Context) Size() (int, int) {
return ctx.window.Size()
}

View File

@@ -120,7 +120,7 @@ func (ti *TextInput) Draw(ctx *Context) {
}
cells := runewidth.StringWidth(string(text[:sindex]) + ti.prompt)
if ti.focus {
ctx.SetCursor(cells, 0)
ctx.SetCursor(cells, 0, vaxis.CursorDefault)
ti.drawPopover(ctx)
}
}
@@ -163,7 +163,7 @@ func (ti *TextInput) Focus(focus bool) {
ti.focus = focus
if focus && ti.ctx != nil {
cells := runewidth.StringWidth(string(ti.text[:ti.index]))
ti.ctx.SetCursor(cells+1, 0)
ti.ctx.SetCursor(cells+1, 0, vaxis.CursorDefault)
} else if !focus && ti.ctx != nil {
ti.ctx.HideCursor()
}

View File

@@ -7,14 +7,15 @@ import (
"syscall"
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rockorager/vaxis"
"github.com/gdamore/tcell/v2"
)
// Use unbuffered channels (always blocking unless somebody can read
// immediately) We are merely using this as a proxy to tcell screen internal
// event channel.
var Events = make(chan tcell.Event)
// immediately) We are merely using this as a proxy to the internal vaxis event
// channel.
var Events = make(chan vaxis.Event)
var Quit = make(chan struct{})
@@ -40,7 +41,7 @@ func Invalidate() {
var state struct {
content DrawableInteractive
ctx *Context
screen tcell.Screen
vx *vaxis.Vaxis
popover *Popover
dirty uint32 // == 1 if render has been queued in Redraw channel
// == 1 if suspend is pending
@@ -60,15 +61,16 @@ func Initialize(content DrawableInteractive) error {
return err
}
screen.Clear()
screen.HideCursor()
screen.EnablePaste()
vx := screen.Vaxis()
width, height := screen.Size()
vx.Window().Clear()
vx.HideCursor()
width, height := vx.Window().Size()
state.content = content
state.screen = screen
state.ctx = NewContext(width, height, state.screen, onPopover)
state.vx = vx
state.ctx = NewContext(width, height, state.vx, onPopover)
Invalidate()
if beeper, ok := content.(DrawableInteractiveBeeper); ok {
@@ -76,7 +78,12 @@ func Initialize(content DrawableInteractive) error {
}
content.Focus(true)
go state.screen.ChannelEvents(Events, Quit)
go func() {
defer log.PanicHandler()
for event := range vx.Events() {
Events <- tcell.TcellEvent(event)
}
}()
return nil
}
@@ -100,7 +107,7 @@ func QueueSuspend() {
func Suspend() error {
var err error
if atomic.SwapUint32(&state.suspending, 0) != 0 {
err = state.screen.Suspend()
err = state.vx.Suspend()
if err == nil {
sigcont := make(chan os.Signal, 1)
signal.Notify(sigcont, syscall.SIGCONT)
@@ -109,21 +116,21 @@ func Suspend() error {
<-sigcont
}
signal.Reset(syscall.SIGCONT)
err = state.screen.Resume()
err = state.vx.Resume()
state.content.Draw(state.ctx)
state.screen.Show()
state.vx.Render()
}
}
return err
}
func Close() {
state.screen.Fini()
state.vx.Close()
}
func Render() {
if atomic.SwapUint32(&state.dirty, 0) != 0 {
state.screen.Clear()
state.vx.Window().Clear()
// reset popover for the next Draw
state.popover = nil
state.content.Draw(state.ctx)
@@ -131,19 +138,15 @@ func Render() {
// if the Draw resulted in a popover, draw it
state.popover.Draw(state.ctx)
}
state.screen.Show()
state.vx.Render()
}
}
func EnableMouse() {
state.screen.EnableMouse()
}
func HandleEvent(event vaxis.Event) {
if event, ok := event.(*tcell.EventResize); ok {
state.screen.Clear()
state.vx.Window().Clear()
width, height := event.Size()
state.ctx = NewContext(width, height, state.screen, onPopover)
state.ctx = NewContext(width, height, state.vx, onPopover)
Invalidate()
}
if event, ok := event.(tcell.VaxisEvent); ok {

View File

@@ -223,9 +223,6 @@ func main() {
}
close(deferLoop)
if config.Ui.MouseEnabled {
ui.EnableMouse()
}
startup, startupDone := context.WithCancel(context.Background())
as, err := ipc.StartServer(app.IPCHandler(), startup)