mirror of
https://git.sr.ht/~rjarry/aerc
synced 2026-03-02 18:23:33 +01:00
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:
committed by
Robin Jarry
parent
63b9706441
commit
787cfbd9a9
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
49
lib/ui/ui.go
49
lib/ui/ui.go
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user