aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorTim Culverhouse <tim@timculverhouse.com>2024-02-12 06:26:16 -0600
committerRobin Jarry <robin@jarry.cc>2024-02-12 13:48:44 +0100
commit787cfbd9a90a26072a8dd61a28eca11dd6e20c04 (patch)
treeb64f887898c810e3de9a9b4dd9c2bc0317cf7902 /lib
parent63b9706441719b53b3504733c51c2387fce9019d (diff)
downloadaerc-787cfbd9a90a26072a8dd61a28eca11dd6e20c04.tar.gz
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>
Diffstat (limited to 'lib')
-rw-r--r--lib/ui/context.go89
-rw-r--r--lib/ui/textinput.go4
-rw-r--r--lib/ui/ui.go49
3 files changed, 73 insertions, 69 deletions
diff --git a/lib/ui/context.go b/lib/ui/context.go
index 39933fa3..10089179 100644
--- a/lib/ui/context.go
+++ b/lib/ui/context.go
@@ -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)
-}
-
-func (ctx *Context) SetCursor(x, y int) {
- ctx.screen.ShowCursor(ctx.x+x, ctx.y+y)
+ 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) 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()
}
diff --git a/lib/ui/textinput.go b/lib/ui/textinput.go
index 0bdcd435..107d354c 100644
--- a/lib/ui/textinput.go
+++ b/lib/ui/textinput.go
@@ -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()
}
diff --git a/lib/ui/ui.go b/lib/ui/ui.go
index e13f4f5c..02e945c0 100644
--- a/lib/ui/ui.go
+++ b/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 {