From 577248f5e15d98a9a6522a605acd434059582bfc Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Sun, 12 May 2019 00:06:09 -0400 Subject: Add initial compose widget --- widgets/compose.go | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 widgets/compose.go (limited to 'widgets/compose.go') diff --git a/widgets/compose.go b/widgets/compose.go new file mode 100644 index 0000000..cf3dac9 --- /dev/null +++ b/widgets/compose.go @@ -0,0 +1,122 @@ +package widgets + +import ( + "os/exec" + + "github.com/gdamore/tcell" + "github.com/mattn/go-runewidth" + + "git.sr.ht/~sircmpwn/aerc2/lib/ui" +) + +type headerEditor struct { + ui.Invalidatable + name string + input *ui.TextInput +} + +type Composer struct { + headers struct { + from *headerEditor + subject *headerEditor + to *headerEditor + } + + editor *Terminal + grid *ui.Grid + + focusable []ui.DrawableInteractive + focused int +} + +// TODO: Let caller configure headers, initial body (for replies), etc +func NewComposer() *Composer { + grid := ui.NewGrid().Rows([]ui.GridSpec{ + {ui.SIZE_EXACT, 3}, + {ui.SIZE_WEIGHT, 1}, + }).Columns([]ui.GridSpec{ + {ui.SIZE_WEIGHT, 1}, + }) + + // TODO: let user specify extra headers to edit by default + headers := ui.NewGrid().Rows([]ui.GridSpec{ + {ui.SIZE_EXACT, 1}, // To/From + {ui.SIZE_EXACT, 1}, // Subject + {ui.SIZE_EXACT, 1}, // [spacer] + }).Columns([]ui.GridSpec{ + {ui.SIZE_WEIGHT, 1}, + {ui.SIZE_WEIGHT, 1}, + }) + + headers.AddChild(newHeaderEditor("To", "Simon Ser ")).At(0, 0) + headers.AddChild(newHeaderEditor("From", "Drew DeVault ")).At(0, 1) + headers.AddChild(newHeaderEditor("Subject", "Re: [PATCH RFC aerc2] widgets: fix StatusLine race")).At(1, 0).Span(1, 2) + headers.AddChild(ui.NewFill(' ')).At(2, 0).Span(1, 2) + + // TODO: built-in config option, $EDITOR, then vi, in that order + // TODO: temp file + editor := exec.Command("vim") + term, _ := NewTerminal(editor) + + grid.AddChild(headers).At(0, 0) + grid.AddChild(term).At(1, 0) + + return &Composer{ + grid: grid, + editor: term, + focused: 0, + focusable: []ui.DrawableInteractive{ + term, + }, + } +} + +func (c *Composer) Draw(ctx *ui.Context) { + c.grid.Draw(ctx) +} + +func (c *Composer) Invalidate() { + c.grid.Invalidate() +} + +func (c *Composer) OnInvalidate(fn func(d ui.Drawable)) { + c.grid.OnInvalidate(func(_ ui.Drawable) { + fn(c) + }) +} + +// TODO: Focus various fields separately +// TODO: Consider having a different set of keybindings for a focused and +// unfocused terminal? +func (c *Composer) Event(event tcell.Event) bool { + if c.editor != nil { + return c.editor.Event(event) + } + return false +} + +func (c *Composer) Focus(focus bool) { + if c.editor != nil { + c.editor.Focus(focus) + } +} + +func newHeaderEditor(name string, value string) *headerEditor { + // TODO: Set default vaule to something sane, I guess + return &headerEditor{ + input: ui.NewTextInput(value), + name: name, + } +} + +func (he *headerEditor) Draw(ctx *ui.Context) { + name := he.name + " " + size := runewidth.StringWidth(name) + ctx.Fill(0, 0, size, ctx.Height(), ' ', tcell.StyleDefault) + ctx.Printf(0, 0, tcell.StyleDefault.Bold(true), "%s", name) + he.input.Draw(ctx.Subcontext(size, 0, ctx.Width()-size, 1)) +} + +func (he *headerEditor) Invalidate() { + he.DoInvalidate(he) +} -- cgit v1.2.3