summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--cmd/aerc/main.go10
-rw-r--r--ui/context.go8
-rw-r--r--ui/exline.go127
-rw-r--r--ui/interactive.go10
-rw-r--r--ui/ui.go11
5 files changed, 162 insertions, 4 deletions
diff --git a/cmd/aerc/main.go b/cmd/aerc/main.go
index c784948..3ba9d1a 100644
--- a/cmd/aerc/main.go
+++ b/cmd/aerc/main.go
@@ -70,9 +70,8 @@ func main() {
 		fill('.'), ui.BORDER_RIGHT)).At(1, 0).Span(2, 1)
 	grid.AddChild(tabs.TabStrip).At(0, 1)
 	grid.AddChild(tabs.TabContent).At(1, 1)
-	// ex line placeholder:
-	grid.AddChild(ui.NewText("Connected").
-		Color(tb.ColorBlack, tb.ColorWhite)).At(2, 1)
+	exline := ui.NewExLine()
+	grid.AddChild(exline).At(2, 1)
 
 	_ui, err := ui.Initialize(conf, grid)
 	if err != nil {
@@ -80,6 +79,8 @@ func main() {
 	}
 	defer _ui.Close()
 
+	_ui.AddInteractive(exline)
+
 	go (func() {
 		for {
 			time.Sleep(1 * time.Second)
@@ -89,7 +90,8 @@ func main() {
 
 	for !_ui.Exit {
 		if !_ui.Tick() {
-			time.Sleep(100 * time.Millisecond)
+			// ~60 FPS
+			time.Sleep(16 * time.Millisecond)
 		}
 	}
 }
diff --git a/ui/context.go b/ui/context.go
index ae9e561..ca3f452 100644
--- a/ui/context.go
+++ b/ui/context.go
@@ -15,6 +15,14 @@ type Context struct {
 	height int
 }
 
+func (ctx *Context) X() int {
+	return ctx.x
+}
+
+func (ctx *Context) Y() int {
+	return ctx.y
+}
+
 func (ctx *Context) Width() int {
 	return ctx.width
 }
diff --git a/ui/exline.go b/ui/exline.go
new file mode 100644
index 0000000..a377cd7
--- /dev/null
+++ b/ui/exline.go
@@ -0,0 +1,127 @@
+package ui
+
+import (
+	tb "github.com/nsf/termbox-go"
+)
+
+// TODO: history
+// TODO: tab completion
+// TODO: commit
+// TODO: cancel (via esc/ctrl+c)
+// TODO: scrolling
+
+type ExLine struct {
+	command *string
+	commit  func(cmd *string)
+	index   int
+	scroll  int
+
+	onInvalidate func(d Drawable)
+}
+
+func NewExLine() *ExLine {
+	cmd := ""
+	return &ExLine{command: &cmd}
+}
+
+func (ex *ExLine) OnInvalidate(onInvalidate func(d Drawable)) {
+	ex.onInvalidate = onInvalidate
+}
+
+func (ex *ExLine) Invalidate() {
+	if ex.onInvalidate != nil {
+		ex.onInvalidate(ex)
+	}
+}
+
+func (ex *ExLine) Draw(ctx *Context) {
+	cell := tb.Cell{
+		Fg: tb.ColorDefault,
+		Bg: tb.ColorDefault,
+		Ch: ' ',
+	}
+	ctx.Fill(0, 0, ctx.Width(), ctx.Height(), cell)
+	ctx.Printf(0, 0, cell, ":%s", *ex.command)
+	tb.SetCursor(ctx.X()+ex.index-ex.scroll+1, ctx.Y())
+}
+
+func (ex *ExLine) insert(ch rune) {
+	newCmd := (*ex.command)[:ex.index] + string(ch) + (*ex.command)[ex.index:]
+	ex.command = &newCmd
+	ex.index++
+	ex.Invalidate()
+}
+
+func (ex *ExLine) deleteWord() {
+	// TODO: Break on any of / " '
+	if len(*ex.command) == 0 {
+		return
+	}
+	i := ex.index - 1
+	if (*ex.command)[i] == ' ' {
+		i--
+	}
+	for ; i >= 0; i-- {
+		if (*ex.command)[i] == ' ' {
+			break
+		}
+	}
+	newCmd := (*ex.command)[:i+1] + (*ex.command)[ex.index:]
+	ex.command = &newCmd
+	ex.index = i + 1
+	ex.Invalidate()
+}
+
+func (ex *ExLine) deleteChar() {
+	if len(*ex.command) > 0 && ex.index != len(*ex.command) {
+		newCmd := (*ex.command)[:ex.index] + (*ex.command)[ex.index+1:]
+		ex.command = &newCmd
+		ex.Invalidate()
+	}
+}
+
+func (ex *ExLine) backspace() {
+	if len(*ex.command) > 0 && ex.index != 0 {
+		newCmd := (*ex.command)[:ex.index-1] + (*ex.command)[ex.index:]
+		ex.command = &newCmd
+		ex.index--
+		ex.Invalidate()
+	}
+}
+
+func (ex *ExLine) Event(event tb.Event) bool {
+	switch event.Type {
+	case tb.EventKey:
+		switch event.Key {
+		case tb.KeySpace:
+			ex.insert(' ')
+		case tb.KeyBackspace, tb.KeyBackspace2:
+			ex.backspace()
+		case tb.KeyCtrlD, tb.KeyDelete:
+			ex.deleteChar()
+		case tb.KeyCtrlB, tb.KeyArrowLeft:
+			if ex.index > 0 {
+				ex.index--
+				ex.Invalidate()
+			}
+		case tb.KeyCtrlF, tb.KeyArrowRight:
+			if ex.index < len(*ex.command) {
+				ex.index++
+				ex.Invalidate()
+			}
+		case tb.KeyCtrlA, tb.KeyHome:
+			ex.index = 0
+			ex.Invalidate()
+		case tb.KeyCtrlE, tb.KeyEnd:
+			ex.index = len(*ex.command)
+			ex.Invalidate()
+		case tb.KeyCtrlW:
+			ex.deleteWord()
+		default:
+			if event.Ch != 0 {
+				ex.insert(event.Ch)
+			}
+		}
+	}
+	return true
+}
diff --git a/ui/interactive.go b/ui/interactive.go
new file mode 100644
index 0000000..5dd5fef
--- /dev/null
+++ b/ui/interactive.go
@@ -0,0 +1,10 @@
+package ui
+
+import (
+	tb "github.com/nsf/termbox-go"
+)
+
+type Interactive interface {
+	// Returns true if the event was handled by this component
+	Event(event tb.Event) bool
+}
diff --git a/ui/ui.go b/ui/ui.go
index d1d2ca3..9ea037c 100644
--- a/ui/ui.go
+++ b/ui/ui.go
@@ -11,6 +11,8 @@ type UI struct {
 	Content Drawable
 	ctx     *Context
 
+	interactive []Interactive
+
 	tbEvents      chan tb.Event
 	invalidations chan interface{}
 }
@@ -58,6 +60,11 @@ func (state *UI) Tick() bool {
 			state.ctx = NewContext(event.Width, event.Height)
 			state.Content.Invalidate()
 		}
+		if state.interactive != nil {
+			for _, i := range state.interactive {
+				i.Event(event)
+			}
+		}
 	case <-state.invalidations:
 		state.Content.Draw(state.ctx)
 		tb.Flush()
@@ -66,3 +73,7 @@ func (state *UI) Tick() bool {
 	}
 	return true
 }
+
+func (state *UI) AddInteractive(i Interactive) {
+	state.interactive = append(state.interactive, i)
+}
-------------------------------------------------- */ #include <HTUtils.h> #include <HTUU.h> #include <LYLeaks.h> static char six2pr[64] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' }; static unsigned char pr2six[256]; /*--- function HTUU_encode ----------------------------------------------- * * Encode a single line of binary data to a standard format that * uses only printing ASCII characters (but takes up 33% more bytes). * * Entry bufin points to a buffer of bytes. If nbytes is not * a multiple of three, then the byte just beyond * the last byte in the buffer must be 0. * nbytes is the number of bytes in that buffer. * This cannot be more than 48. * bufcoded points to an output buffer. Be sure that this * can hold at least 1 + (4*nbytes)/3 characters. * * Exit bufcoded contains the coded line. The first 4*nbytes/3 bytes * contain printing ASCII characters representing * those binary bytes. This may include one or * two '=' characters used as padding at the end. * The last byte is a zero byte. * Returns the number of ASCII characters in "bufcoded". */ int HTUU_encode(unsigned char *bufin, size_t nbytes, char *bufcoded) { /* ENC is the basic 1 character encoding function to make a char printing */ #define ENC(c) six2pr[c] register char *outptr = bufcoded; size_t i; /* This doesn't seem to be needed (AL): register unsigned char *inptr = bufin; */ for (i = 0; i < nbytes; i += 3) { *(outptr++) = ENC(*bufin >> 2); /* c1 */ *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2 */ *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /*c3 */ *(outptr++) = ENC(bufin[2] & 077); /* c4 */ bufin += 3; } /* If nbytes was not a multiple of 3, then we have encoded too * many characters. Adjust appropriately. */ if (i == nbytes + 1) { /* There were only 2 bytes in that last group */ outptr[-1] = '='; } else if (i == nbytes + 2) { /* There was only 1 byte in that last group */ outptr[-1] = '='; outptr[-2] = '='; } *outptr = '\0'; return (int) (outptr - bufcoded); } /*--- function HTUU_decode ------------------------------------------------ * * Decode an ASCII-encoded buffer back to its original binary form. * * Entry bufcoded points to a uuencoded string. It is * terminated by any character not in * the printable character table six2pr, but * leading whitespace is stripped. * bufplain points to the output buffer; must be big * enough to hold the decoded string (generally * shorter than the encoded string) plus * as many as two extra bytes used during * the decoding process. * outbufsize is the maximum number of bytes that * can fit in bufplain. * * Exit Returns the number of binary bytes decoded. * bufplain contains these bytes. */ int HTUU_decode(char *bufcoded, unsigned char *bufplain, int outbufsize) { /* single character decode */ #define DEC(c) pr2six[(int)c] #define MAXVAL 63 static int first = 1; int nbytesdecoded, j; register char *bufin; register unsigned char *bufout = bufplain; register int nprbytes; /* If this is the first call, initialize the mapping table. * This code should work even on non-ASCII machines. */ if (first) { first = 0; for (j = 0; j < 256; j++) pr2six[j] = MAXVAL + 1; for (j = 0; j < 64; j++) pr2six[UCH(six2pr[j])] = UCH(j); #if 0 /* *INDENT-OFF* */ pr2six['A']= 0; pr2six['B']= 1; pr2six['C']= 2; pr2six['D']= 3; pr2six['E']= 4; pr2six['F']= 5; pr2six['G']= 6; pr2six['H']= 7; pr2six['I']= 8; pr2six['J']= 9; pr2six['K']=10; pr2six['L']=11; pr2six['M']=12; pr2six['N']=13; pr2six['O']=14; pr2six['P']=15; pr2six['Q']=16; pr2six['R']=17; pr2six['S']=18; pr2six['T']=19; pr2six['U']=20; pr2six['V']=21; pr2six['W']=22; pr2six['X']=23; pr2six['Y']=24; pr2six['Z']=25; pr2six['a']=26; pr2six['b']=27; pr2six['c']=28; pr2six['d']=29; pr2six['e']=30; pr2six['f']=31; pr2six['g']=32; pr2six['h']=33; pr2six['i']=34; pr2six['j']=35; pr2six['k']=36; pr2six['l']=37; pr2six['m']=38; pr2six['n']=39; pr2six['o']=40; pr2six['p']=41; pr2six['q']=42; pr2six['r']=43; pr2six['s']=44; pr2six['t']=45; pr2six['u']=46; pr2six['v']=47; pr2six['w']=48; pr2six['x']=49; pr2six['y']=50; pr2six['z']=51; pr2six['0']=52; pr2six['1']=53; pr2six['2']=54; pr2six['3']=55; pr2six['4']=56; pr2six['5']=57; pr2six['6']=58; pr2six['7']=59; pr2six['8']=60; pr2six['9']=61; pr2six['+']=62; pr2six['/']=63; /* *INDENT-ON* */ #endif } /* Strip leading whitespace. */ while (*bufcoded == ' ' || *bufcoded == '\t') bufcoded++; /* Figure out how many characters are in the input buffer. * If this would decode into more bytes than would fit into * the output buffer, adjust the number of input bytes downwards. */ bufin = bufcoded; while (pr2six[UCH(*(bufin++))] <= MAXVAL) ; nprbytes = (int) (bufin - bufcoded - 1); nbytesdecoded = ((nprbytes + 3) / 4) * 3; if (nbytesdecoded > outbufsize) { nprbytes = (outbufsize * 4) / 3; } bufin = bufcoded; while (nprbytes > 0) { *(bufout++) = UCH((DEC(bufin[0]) << 2) | (DEC(bufin[1]) >> 4)); *(bufout++) = UCH((DEC(bufin[1]) << 4) | (DEC(bufin[2]) >> 2)); *(bufout++) = UCH((DEC(bufin[2]) << 6) | (DEC(bufin[3]))); bufin += 4; nprbytes -= 4; } if (nprbytes & 03) { if (pr2six[(int) bufin[-2]] > MAXVAL) { nbytesdecoded -= 2; } else { nbytesdecoded -= 1; } } return (nbytesdecoded); }